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  // A full inner node is serialized as 16 256-bit hashes, back to back:
136  if (data.size() != branchFactor * uint256::bytes)
137  Throw<std::runtime_error>("Invalid FI node");
138 
139  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
140 
141  SerialIter si(data);
142 
143  auto hashes = ret->hashesAndChildren_.getHashes();
144 
145  for (int i = 0; i < branchFactor; ++i)
146  {
147  hashes[i].as_uint256() = si.getBitString<256>();
148 
149  if (hashes[i].isNonZero())
150  ret->isBranch_ |= (1 << i);
151  }
152 
153  ret->resizeChildArrays(ret->getBranchCount());
154 
155  if (hashValid)
156  ret->hash_ = hash;
157  else
158  ret->updateHash();
159 
160  return ret;
161 }
162 
165 {
166  // A compressed inner node is serialized as a series of 33 byte chunks,
167  // representing a one byte "position" and a 256-bit hash:
168  constexpr std::size_t chunkSize = uint256::bytes + 1;
169 
170  if (auto const s = data.size();
171  (s % chunkSize != 0) || (s > chunkSize * branchFactor))
172  Throw<std::runtime_error>("Invalid CI node");
173 
174  SerialIter si(data);
175 
176  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
177 
178  auto hashes = ret->hashesAndChildren_.getHashes();
179 
180  while (!si.empty())
181  {
182  auto const hash = si.getBitString<256>();
183  auto const pos = si.get8();
184 
185  if (pos >= branchFactor)
186  Throw<std::runtime_error>("invalid CI node");
187 
188  hashes[pos].as_uint256() = hash;
189 
190  if (hashes[pos].isNonZero())
191  ret->isBranch_ |= (1 << pos);
192  }
193 
194  ret->resizeChildArrays(ret->getBranchCount());
195  ret->updateHash();
196  return ret;
197 }
198 
199 void
201 {
202  uint256 nh;
203  if (isBranch_ != 0)
204  {
206  using beast::hash_append;
208  iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
209  nh = static_cast<typename sha512_half_hasher::result_type>(h);
210  }
211  hash_ = SHAMapHash{nh};
212 }
213 
214 void
216 {
217  SHAMapHash* hashes;
219  // structured bindings can't be captured in c++ 17; use tie instead
220  std::tie(std::ignore, hashes, children) =
222  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
223  if (children[indexNum] != nullptr)
224  hashes[indexNum] = children[indexNum]->getHash();
225  });
226  updateHash();
227 }
228 
229 void
231 {
232  assert(!isEmpty());
233 
234  // If the node is sparse, then only send non-empty branches:
235  if (getBranchCount() < 12)
236  {
237  // compressed node
238  auto hashes = hashesAndChildren_.getHashes();
239  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
240  s.addBitString(hashes[indexNum].as_uint256());
241  s.add8(branchNum);
242  });
244  }
245  else
246  {
247  iterChildren(
248  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
249  s.add8(wireTypeInner);
250  }
251 }
252 
253 void
255 {
256  assert(!isEmpty());
257 
259  iterChildren(
260  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
261 }
262 
263 bool
265 {
266  return isBranch_ == 0;
267 }
268 
269 int
271 {
272  return popcnt16(isBranch_);
273 }
274 
277 {
279  auto hashes = hashesAndChildren_.getHashes();
280  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
281  ret += "\nb";
282  ret += std::to_string(branchNum);
283  ret += " = ";
284  ret += to_string(hashes[indexNum]);
285  });
286  return ret;
287 }
288 
289 // We are modifying an inner node
290 void
292 {
293  assert((m >= 0) && (m < branchFactor));
294  assert(cowid_ != 0);
295  assert(child.get() != this);
296 
297  auto const dstIsBranch = [&] {
298  if (child)
299  return isBranch_ | (1 << m);
300  else
301  return isBranch_ & ~(1 << m);
302  }();
303 
304  auto const dstToAllocate = popcnt16(dstIsBranch);
305  // change hashesAndChildren to remove the element, or make room for the
306  // added element, if necessary
308  std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
309 
310  isBranch_ = dstIsBranch;
311 
312  if (child)
313  {
314  auto const childIndex = *getChildIndex(m);
315  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
316  hashes[childIndex].zero();
317  children[childIndex] = child;
318  }
319 
320  hash_.zero();
321 
323 }
324 
325 // finished modifying, now make shareable
326 void
328 {
329  assert((m >= 0) && (m < branchFactor));
330  assert(cowid_ != 0);
331  assert(child);
332  assert(child.get() != this);
333 
334  assert(!isEmptyBranch(m));
336 }
337 
340 {
341  assert(branch >= 0 && branch < branchFactor);
342  assert(!isEmptyBranch(branch));
343 
345  return hashesAndChildren_.getChildren()[*getChildIndex(branch)].get();
346 }
347 
350 {
351  assert(branch >= 0 && branch < branchFactor);
352  assert(!isEmptyBranch(branch));
353 
355  return hashesAndChildren_.getChildren()[*getChildIndex(branch)];
356 }
357 
358 SHAMapHash const&
360 {
361  assert((m >= 0) && (m < branchFactor));
362  if (auto const i = getChildIndex(m))
363  return hashesAndChildren_.getHashes()[*i];
364 
365  return zeroSHAMapHash;
366 }
367 
370  int branch,
372 {
373  assert(branch >= 0 && branch < branchFactor);
374  assert(node);
375  assert(!isEmptyBranch(branch));
376  auto const childIndex = *getChildIndex(branch);
377  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
378  assert(node->getHash() == hashes[childIndex]);
379 
381  if (children[childIndex])
382  {
383  // There is already a node hooked up, return it
384  node = children[childIndex];
385  }
386  else
387  {
388  // Hook this node up
389  children[childIndex] = node;
390  }
391  return node;
392 }
393 
394 void
395 SHAMapInnerNode::invariants(bool is_root) const
396 {
397  unsigned count = 0;
398  auto [numAllocated, hashes, children] =
400 
401  if (numAllocated != branchFactor)
402  {
403  auto const branchCount = getBranchCount();
404  for (int i = 0; i < branchCount; ++i)
405  {
406  assert(hashes[i].isNonZero());
407  if (children[i] != nullptr)
408  children[i]->invariants();
409  ++count;
410  }
411  }
412  else
413  {
414  for (int i = 0; i < branchFactor; ++i)
415  {
416  if (hashes[i].isNonZero())
417  {
418  assert((isBranch_ & (1 << i)) != 0);
419  if (children[i] != nullptr)
420  children[i]->invariants();
421  ++count;
422  }
423  else
424  {
425  assert((isBranch_ & (1 << i)) == 0);
426  }
427  }
428  }
429 
430  if (!is_root)
431  {
432  assert(hash_.isNonZero());
433  assert(count >= 1);
434  }
435  assert((count == 0) ? hash_.isZero() : hash_.isNonZero());
436 }
437 
438 } // namespace ripple
ripple::SHAMapTreeNode::cowid
std::uint32_t cowid() const
Returns the SHAMap that owns this node.
Definition: SHAMapTreeNode.h:116
ripple::SHAMapInnerNode::serializeWithPrefix
void serializeWithPrefix(Serializer &) const override
Serialize the node in a format appropriate for hashing.
Definition: SHAMapInnerNode.cpp:254
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:291
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:349
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:369
ripple::SHAMapTreeNode::cowid_
std::uint32_t cowid_
Determines the owning SHAMap, if any.
Definition: SHAMapTreeNode.h:64
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:200
iterator
std::lock_guard
STL class.
ripple::SHAMapHash::isZero
bool isZero() const
Definition: SHAMapHash.h:53
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:327
ripple::wireTypeInner
static constexpr unsigned const char wireTypeInner
Definition: SHAMapTreeNode.h:42
ripple::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:33
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapHash.h:58
ripple::SHAMapTreeNode::getString
virtual std::string getString(SHAMapNodeID const &) const
Definition: SHAMapTreeNode.cpp:184
ripple::SHAMapTreeNode::hash_
SHAMapHash hash_
Definition: SHAMapTreeNode.h:56
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: SHAMapHash.h:32
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:276
ripple::HashPrefix::innerNode
@ innerNode
inner node in V1 tree
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:359
ripple::base_uint< 256 >::bytes
static constexpr std::size_t bytes
Definition: base_uint.h:98
ripple::SerialIter::get8
unsigned char get8()
Definition: Serializer.cpp:362
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::SerialIter::empty
std::size_t empty() const noexcept
Definition: Serializer.h:332
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:53
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::SHAMapInnerNode::updateHashDeep
void updateHashDeep()
Recalculate the hash of all children and this node.
Definition: SHAMapInnerNode.cpp:215
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::SerialIter
Definition: Serializer.h:310
ripple::SHAMapInnerNode::getBranchCount
int getBranchCount() const
Definition: SHAMapInnerNode.cpp:270
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:143
ripple::Serializer
Definition: Serializer.h:39
ripple::wireTypeCompressedInner
static constexpr unsigned const char wireTypeCompressedInner
Definition: SHAMapTreeNode.h:43
ripple::SerialIter::getBitString
base_uint< Bits, Tag > getBitString()
Definition: Serializer.h:414
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:230
ripple::Serializer::addBitString
int addBitString(base_uint< Bits, Tag > const &v)
Definition: Serializer.h:97
ripple::SHAMapHash::zero
void zero()
Definition: SHAMapHash.h:68
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.
std::size_t
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:264
ripple::SHAMapInnerNode::makeCompressedInner
static std::shared_ptr< SHAMapTreeNode > makeCompressedInner(Slice data)
Definition: SHAMapInnerNode.cpp:164
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: SHAMapHash.h:43
ripple::SHAMapInnerNode::getChildPointer
SHAMapTreeNode * getChildPointer(int branch)
Definition: SHAMapInnerNode.cpp:339
ripple::TaggedPointer::capacity
std::uint8_t capacity() const
Get the number of elements allocated for each array.
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:395
ripple::SHAMapInnerNode::fullBelowGen_
std::uint32_t fullBelowGen_
Definition: SHAMapInnerNode.h:53