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/Log.h>
23 #include <ripple/basics/Slice.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/basics/spinlock.h>
26 #include <ripple/beast/core/LexicalCast.h>
27 #include <ripple/protocol/HashPrefix.h>
28 #include <ripple/protocol/digest.h>
29 #include <ripple/shamap/SHAMapTreeNode.h>
30 #include <ripple/shamap/impl/TaggedPointer.ipp>
31 
32 #include <algorithm>
33 #include <iterator>
34 #include <utility>
35 
36 namespace ripple {
37 
39  std::uint32_t cowid,
40  std::uint8_t numAllocatedChildren)
41  : SHAMapTreeNode(cowid), hashesAndChildren_(numAllocatedChildren)
42 {
43 }
44 
46 
47 template <class F>
48 void
50 {
51  hashesAndChildren_.iterChildren(isBranch_, std::forward<F>(f));
52 }
53 
54 template <class F>
55 void
57 {
59 }
60 
61 void
63 {
65  TaggedPointer(std::move(hashesAndChildren_), isBranch_, toAllocate);
66 }
67 
70 {
72 }
73 
76 {
77  auto const branchCount = getBranchCount();
78  auto const thisIsSparse = !hashesAndChildren_.isDense();
79  auto p = std::make_shared<SHAMapInnerNode>(cowid, branchCount);
80  p->hash_ = hash_;
81  p->isBranch_ = isBranch_;
82  p->fullBelowGen_ = fullBelowGen_;
83  SHAMapHash *cloneHashes, *thisHashes;
84  std::shared_ptr<SHAMapTreeNode>*cloneChildren, *thisChildren;
85  // structured bindings can't be captured in c++ 17; use tie instead
86  std::tie(std::ignore, cloneHashes, cloneChildren) =
87  p->hashesAndChildren_.getHashesAndChildren();
88  std::tie(std::ignore, thisHashes, thisChildren) =
90 
91  if (thisIsSparse)
92  {
93  int cloneChildIndex = 0;
94  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
95  cloneHashes[cloneChildIndex++] = thisHashes[indexNum];
96  });
97  }
98  else
99  {
100  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
101  cloneHashes[branchNum] = thisHashes[indexNum];
102  });
103  }
104 
105  spinlock sl(lock_);
106  std::lock_guard lock(sl);
107 
108  if (thisIsSparse)
109  {
110  int cloneChildIndex = 0;
111  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
112  cloneChildren[cloneChildIndex++] = thisChildren[indexNum];
113  });
114  }
115  else
116  {
117  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
118  cloneChildren[branchNum] = thisChildren[indexNum];
119  });
120  }
121 
122  return p;
123 }
124 
127  Slice data,
128  SHAMapHash const& hash,
129  bool hashValid)
130 {
131  // A full inner node is serialized as 16 256-bit hashes, back to back:
132  if (data.size() != branchFactor * uint256::bytes)
133  Throw<std::runtime_error>("Invalid FI node");
134 
135  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
136 
137  SerialIter si(data);
138 
139  auto hashes = ret->hashesAndChildren_.getHashes();
140 
141  for (int i = 0; i < branchFactor; ++i)
142  {
143  hashes[i].as_uint256() = si.getBitString<256>();
144 
145  if (hashes[i].isNonZero())
146  ret->isBranch_ |= (1 << i);
147  }
148 
149  ret->resizeChildArrays(ret->getBranchCount());
150 
151  if (hashValid)
152  ret->hash_ = hash;
153  else
154  ret->updateHash();
155 
156  return ret;
157 }
158 
161 {
162  // A compressed inner node is serialized as a series of 33 byte chunks,
163  // representing a one byte "position" and a 256-bit hash:
164  constexpr std::size_t chunkSize = uint256::bytes + 1;
165 
166  if (auto const s = data.size();
167  (s % chunkSize != 0) || (s > chunkSize * branchFactor))
168  Throw<std::runtime_error>("Invalid CI node");
169 
170  SerialIter si(data);
171 
172  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
173 
174  auto hashes = ret->hashesAndChildren_.getHashes();
175 
176  while (!si.empty())
177  {
178  auto const hash = si.getBitString<256>();
179  auto const pos = si.get8();
180 
181  if (pos >= branchFactor)
182  Throw<std::runtime_error>("invalid CI node");
183 
184  hashes[pos].as_uint256() = hash;
185 
186  if (hashes[pos].isNonZero())
187  ret->isBranch_ |= (1 << pos);
188  }
189 
190  ret->resizeChildArrays(ret->getBranchCount());
191  ret->updateHash();
192  return ret;
193 }
194 
195 void
197 {
198  uint256 nh;
199  if (isBranch_ != 0)
200  {
202  using beast::hash_append;
204  iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
205  nh = static_cast<typename sha512_half_hasher::result_type>(h);
206  }
207  hash_ = SHAMapHash{nh};
208 }
209 
210 void
212 {
213  SHAMapHash* hashes;
215  // structured bindings can't be captured in c++ 17; use tie instead
216  std::tie(std::ignore, hashes, children) =
218  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
219  if (children[indexNum] != nullptr)
220  hashes[indexNum] = children[indexNum]->getHash();
221  });
222  updateHash();
223 }
224 
225 void
227 {
228  assert(!isEmpty());
229 
230  // If the node is sparse, then only send non-empty branches:
231  if (getBranchCount() < 12)
232  {
233  // compressed node
234  auto hashes = hashesAndChildren_.getHashes();
235  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
236  s.addBitString(hashes[indexNum].as_uint256());
237  s.add8(branchNum);
238  });
240  }
241  else
242  {
243  iterChildren(
244  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
245  s.add8(wireTypeInner);
246  }
247 }
248 
249 void
251 {
252  assert(!isEmpty());
253 
255  iterChildren(
256  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
257 }
258 
259 bool
261 {
262  return isBranch_ == 0;
263 }
264 
265 int
267 {
268  return popcnt16(isBranch_);
269 }
270 
273 {
275  auto hashes = hashesAndChildren_.getHashes();
276  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
277  ret += "\nb";
278  ret += std::to_string(branchNum);
279  ret += " = ";
280  ret += to_string(hashes[indexNum]);
281  });
282  return ret;
283 }
284 
285 // We are modifying an inner node
286 void
288 {
289  assert((m >= 0) && (m < branchFactor));
290  assert(cowid_ != 0);
291  assert(child.get() != this);
292 
293  auto const dstIsBranch = [&] {
294  if (child)
295  return isBranch_ | (1 << m);
296  else
297  return isBranch_ & ~(1 << m);
298  }();
299 
300  auto const dstToAllocate = popcnt16(dstIsBranch);
301  // change hashesAndChildren to remove the element, or make room for the
302  // added element, if necessary
304  std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
305 
306  isBranch_ = dstIsBranch;
307 
308  if (child)
309  {
310  auto const childIndex = *getChildIndex(m);
311  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
312  hashes[childIndex].zero();
313  children[childIndex] = std::move(child);
314  }
315 
316  hash_.zero();
317 
319 }
320 
321 // finished modifying, now make shareable
322 void
324 {
325  assert((m >= 0) && (m < branchFactor));
326  assert(cowid_ != 0);
327  assert(child);
328  assert(child.get() != this);
329 
330  assert(!isEmptyBranch(m));
332 }
333 
336 {
337  assert(branch >= 0 && branch < branchFactor);
338  assert(!isEmptyBranch(branch));
339 
340  auto const index = *getChildIndex(branch);
341 
342  packed_spinlock sl(lock_, index);
343  std::lock_guard lock(sl);
344  return hashesAndChildren_.getChildren()[index].get();
345 }
346 
349 {
350  assert(branch >= 0 && branch < branchFactor);
351  assert(!isEmptyBranch(branch));
352 
353  auto const index = *getChildIndex(branch);
354 
355  packed_spinlock sl(lock_, index);
356  std::lock_guard lock(sl);
357  return hashesAndChildren_.getChildren()[index];
358 }
359 
360 SHAMapHash const&
362 {
363  assert((m >= 0) && (m < branchFactor));
364  if (auto const i = getChildIndex(m))
365  return hashesAndChildren_.getHashes()[*i];
366 
367  return zeroSHAMapHash;
368 }
369 
372  int branch,
374 {
375  assert(branch >= 0 && branch < branchFactor);
376  assert(node);
377  assert(!isEmptyBranch(branch));
378  auto const childIndex = *getChildIndex(branch);
379  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
380  assert(node->getHash() == hashes[childIndex]);
381 
382  packed_spinlock sl(lock_, childIndex);
383  std::lock_guard lock(sl);
384 
385  if (children[childIndex])
386  {
387  // There is already a node hooked up, return it
388  node = children[childIndex];
389  }
390  else
391  {
392  // Hook this node up
393  children[childIndex] = node;
394  }
395  return node;
396 }
397 
398 void
399 SHAMapInnerNode::invariants(bool is_root) const
400 {
401  [[maybe_unused]] unsigned count = 0;
402  auto [numAllocated, hashes, children] =
404 
405  if (numAllocated != branchFactor)
406  {
407  auto const branchCount = getBranchCount();
408  for (int i = 0; i < branchCount; ++i)
409  {
410  assert(hashes[i].isNonZero());
411  if (children[i] != nullptr)
412  children[i]->invariants();
413  ++count;
414  }
415  }
416  else
417  {
418  for (int i = 0; i < branchFactor; ++i)
419  {
420  if (hashes[i].isNonZero())
421  {
422  assert((isBranch_ & (1 << i)) != 0);
423  if (children[i] != nullptr)
424  children[i]->invariants();
425  ++count;
426  }
427  else
428  {
429  assert((isBranch_ & (1 << i)) == 0);
430  }
431  }
432  }
433 
434  if (!is_root)
435  {
436  assert(hash_.isNonZero());
437  assert(count >= 1);
438  }
439  assert((count == 0) ? hash_.isZero() : hash_.isNonZero());
440 }
441 
442 } // 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:250
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:75
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:53
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:348
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:126
ripple::SHAMapInnerNode::canonicalizeChild
std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:371
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:196
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:46
ripple::SHAMapInnerNode::shareChild
void shareChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:323
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:49
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:198
ripple::SHAMapInnerNode::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(F &&f) const
Call the f callback for all non-empty branches.
Definition: SHAMapInnerNode.cpp:56
ripple::base_uint< 256 >
ripple::SHAMapInnerNode::getString
std::string getString(SHAMapNodeID const &) const override
Definition: SHAMapInnerNode.cpp:272
ripple::HashPrefix::innerNode
@ innerNode
inner node in V1 tree
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:361
ripple::base_uint< 256 >::bytes
static constexpr std::size_t bytes
Definition: base_uint.h:105
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > child)
Definition: SHAMapInnerNode.cpp:287
ripple::SerialIter::get8
unsigned char get8()
Definition: Serializer.cpp:362
ripple::packed_spinlock
Classes to handle arrays of spinlocks packed into a single atomic integer:
Definition: spinlock.h:89
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:62
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:69
ripple::SHAMapInnerNode::updateHashDeep
void updateHashDeep()
Recalculate the hash of all children and this node.
Definition: SHAMapInnerNode.cpp:211
ripple::SHAMapInnerNode::SHAMapInnerNode
SHAMapInnerNode(std::uint32_t cowid, std::uint8_t numAllocatedChildren=2)
Definition: SHAMapInnerNode.cpp:38
ripple::SHAMapInnerNode::isBranch_
std::uint16_t isBranch_
Definition: SHAMapInnerNode.h:56
ripple::SerialIter
Definition: Serializer.h:310
ripple::SHAMapInnerNode::getBranchCount
int getBranchCount() const
Definition: SHAMapInnerNode.cpp:266
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:226
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::spinlock
A spinlock implemented on top of an atomic integer.
Definition: spinlock.h:163
std::optional< int >
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:41
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:260
ripple::SHAMapInnerNode::makeCompressedInner
static std::shared_ptr< SHAMapTreeNode > makeCompressedInner(Slice data)
Definition: SHAMapInnerNode.cpp:160
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:335
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::lock_
std::atomic< std::uint16_t > lock_
A bitlock for the children of this node, with one bit per child.
Definition: SHAMapInnerNode.h:59
ripple::SHAMapInnerNode::invariants
void invariants(bool is_root=false) const override
Definition: SHAMapInnerNode.cpp:399
ripple::SHAMapInnerNode::fullBelowGen_
std::uint32_t fullBelowGen_
Definition: SHAMapInnerNode.h:55