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 
261 {
263  auto hashes = hashesAndChildren_.getHashes();
264  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
265  ret += "\nb";
266  ret += std::to_string(branchNum);
267  ret += " = ";
268  ret += to_string(hashes[indexNum]);
269  });
270  return ret;
271 }
272 
273 // We are modifying an inner node
274 void
276 {
277  assert((m >= 0) && (m < branchFactor));
278  assert(cowid_ != 0);
279  assert(child.get() != this);
280 
281  auto const dstIsBranch = [&] {
282  if (child)
283  return isBranch_ | (1u << m);
284  else
285  return isBranch_ & ~(1u << m);
286  }();
287 
288  auto const dstToAllocate = popcnt16(dstIsBranch);
289  // change hashesAndChildren to remove the element, or make room for the
290  // added element, if necessary
292  std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
293 
294  isBranch_ = dstIsBranch;
295 
296  if (child)
297  {
298  auto const childIndex = *getChildIndex(m);
299  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
300  hashes[childIndex].zero();
301  children[childIndex] = std::move(child);
302  }
303 
304  hash_.zero();
305 
307 }
308 
309 // finished modifying, now make shareable
310 void
312 {
313  assert((m >= 0) && (m < branchFactor));
314  assert(cowid_ != 0);
315  assert(child);
316  assert(child.get() != this);
317 
318  assert(!isEmptyBranch(m));
320 }
321 
324 {
325  assert(branch >= 0 && branch < branchFactor);
326  assert(!isEmptyBranch(branch));
327 
328  auto const index = *getChildIndex(branch);
329 
330  packed_spinlock sl(lock_, index);
331  std::lock_guard lock(sl);
332  return hashesAndChildren_.getChildren()[index].get();
333 }
334 
337 {
338  assert(branch >= 0 && branch < branchFactor);
339  assert(!isEmptyBranch(branch));
340 
341  auto const index = *getChildIndex(branch);
342 
343  packed_spinlock sl(lock_, index);
344  std::lock_guard lock(sl);
345  return hashesAndChildren_.getChildren()[index];
346 }
347 
348 SHAMapHash const&
350 {
351  assert((m >= 0) && (m < branchFactor));
352  if (auto const i = getChildIndex(m))
353  return hashesAndChildren_.getHashes()[*i];
354 
355  return zeroSHAMapHash;
356 }
357 
360  int branch,
362 {
363  assert(branch >= 0 && branch < branchFactor);
364  assert(node);
365  assert(!isEmptyBranch(branch));
366  auto const childIndex = *getChildIndex(branch);
367  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
368  assert(node->getHash() == hashes[childIndex]);
369 
370  packed_spinlock sl(lock_, childIndex);
371  std::lock_guard lock(sl);
372 
373  if (children[childIndex])
374  {
375  // There is already a node hooked up, return it
376  node = children[childIndex];
377  }
378  else
379  {
380  // Hook this node up
381  children[childIndex] = node;
382  }
383  return node;
384 }
385 
386 void
387 SHAMapInnerNode::invariants(bool is_root) const
388 {
389  [[maybe_unused]] unsigned count = 0;
390  auto [numAllocated, hashes, children] =
392 
393  if (numAllocated != branchFactor)
394  {
395  auto const branchCount = getBranchCount();
396  for (int i = 0; i < branchCount; ++i)
397  {
398  assert(hashes[i].isNonZero());
399  if (children[i] != nullptr)
400  children[i]->invariants();
401  ++count;
402  }
403  }
404  else
405  {
406  for (int i = 0; i < branchFactor; ++i)
407  {
408  if (hashes[i].isNonZero())
409  {
410  assert((isBranch_ & (1 << i)) != 0);
411  if (children[i] != nullptr)
412  children[i]->invariants();
413  ++count;
414  }
415  else
416  {
417  assert((isBranch_ & (1 << i)) == 0);
418  }
419  }
420  }
421 
422  if (!is_root)
423  {
424  assert(hash_.isNonZero());
425  assert(count >= 1);
426  }
427  assert((count == 0) ? hash_.isZero() : hash_.isNonZero());
428 }
429 
430 } // 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::popcnt16
int popcnt16(std::uint16_t a)
Definition: TaggedPointer.h:223
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:54
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:336
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:359
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:47
ripple::SHAMapInnerNode::shareChild
void shareChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:311
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:59
ripple::SHAMapHash
Definition: SHAMapHash.h:32
algorithm
std::tie
T tie(T... args)
ripple::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:205
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:260
ripple::HashPrefix::innerNode
@ innerNode
inner node in V1 tree
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:349
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:275
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:333
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:57
ripple::SerialIter
Definition: Serializer.h:311
ripple::SHAMapInnerNode::getBranchCount
int getBranchCount() const
Definition: SHAMapInnerNode.h:211
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:40
ripple::wireTypeCompressedInner
static constexpr unsigned const char wireTypeCompressedInner
Definition: SHAMapTreeNode.h:43
ripple::SerialIter::getBitString
base_uint< Bits, Tag > getBitString()
Definition: Serializer.h:415
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:98
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.h:199
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:323
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:60
ripple::SHAMapInnerNode::invariants
void invariants(bool is_root=false) const override
Definition: SHAMapInnerNode.cpp:387
ripple::SHAMapInnerNode::fullBelowGen_
std::uint32_t fullBelowGen_
Definition: SHAMapInnerNode.h:56