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 
52 template <class F>
53 void
55 {
56  hashesAndChildren_.iterChildren(isBranch_, std::forward<F>(f));
57 }
58 
59 template <class F>
60 void
62 {
64 }
65 
66 void
68 {
70  TaggedPointer(std::move(hashesAndChildren_), isBranch_, toAllocate);
71 }
72 
75 {
77 }
78 
81 {
82  auto const branchCount = getBranchCount();
83  auto const thisIsSparse = !hashesAndChildren_.isDense();
84  auto p = std::make_shared<SHAMapInnerNode>(cowid, branchCount);
85  p->hash_ = hash_;
86  p->isBranch_ = isBranch_;
87  p->fullBelowGen_ = fullBelowGen_;
88  SHAMapHash *cloneHashes, *thisHashes;
89  std::shared_ptr<SHAMapTreeNode>*cloneChildren, *thisChildren;
90  // structured bindings can't be captured in c++ 17; use tie instead
91  std::tie(std::ignore, cloneHashes, cloneChildren) =
92  p->hashesAndChildren_.getHashesAndChildren();
93  std::tie(std::ignore, thisHashes, thisChildren) =
95 
96  if (thisIsSparse)
97  {
98  int cloneChildIndex = 0;
99  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
100  cloneHashes[cloneChildIndex++] = thisHashes[indexNum];
101  });
102  }
103  else
104  {
105  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
106  cloneHashes[branchNum] = thisHashes[indexNum];
107  });
108  }
110  if (thisIsSparse)
111  {
112  int cloneChildIndex = 0;
113  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
114  cloneChildren[cloneChildIndex++] = thisChildren[indexNum];
115  });
116  }
117  else
118  {
119  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
120  cloneChildren[branchNum] = thisChildren[indexNum];
121  });
122  }
123 
124  return p;
125 }
126 
129  Slice data,
130  SHAMapHash const& hash,
131  bool hashValid)
132 {
133  if (data.size() != 512)
134  Throw<std::runtime_error>("Invalid FI node");
135 
136  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
137 
138  Serializer s(data.data(), data.size());
139 
140  auto retHashes = ret->hashesAndChildren_.getHashes();
141  for (int i = 0; i < branchFactor; ++i)
142  {
143  s.getBitString(retHashes[i].as_uint256(), i * 32);
144 
145  if (retHashes[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  return ret;
156 }
157 
160 {
161  Serializer s(data.data(), data.size());
162 
163  int len = s.getLength();
164 
165  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
166 
167  auto retHashes = ret->hashesAndChildren_.getHashes();
168  for (int i = 0; i < (len / 33); ++i)
169  {
170  int pos;
171 
172  if (!s.get8(pos, 32 + (i * 33)))
173  Throw<std::runtime_error>("short CI node");
174 
175  if ((pos < 0) || (pos >= branchFactor))
176  Throw<std::runtime_error>("invalid CI node");
177 
178  s.getBitString(retHashes[pos].as_uint256(), i * 33);
179 
180  if (retHashes[pos].isNonZero())
181  ret->isBranch_ |= (1 << pos);
182  }
183 
184  ret->resizeChildArrays(ret->getBranchCount());
185 
186  ret->updateHash();
187 
188  return ret;
189 }
190 
191 void
193 {
194  uint256 nh;
195  if (isBranch_ != 0)
196  {
198  using beast::hash_append;
200  iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
201  nh = static_cast<typename sha512_half_hasher::result_type>(h);
202  }
203  hash_ = SHAMapHash{nh};
204 }
205 
206 void
208 {
209  SHAMapHash* hashes;
211  // structured bindings can't be captured in c++ 17; use tie instead
212  std::tie(std::ignore, hashes, children) =
214  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
215  if (children[indexNum] != nullptr)
216  hashes[indexNum] = children[indexNum]->getHash();
217  });
218  updateHash();
219 }
220 
221 void
223 {
224  assert(!isEmpty());
225 
226  // If the node is sparse, then only send non-empty branches:
227  if (getBranchCount() < 12)
228  {
229  // compressed node
230  auto hashes = hashesAndChildren_.getHashes();
231  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
232  s.addBitString(hashes[indexNum].as_uint256());
233  s.add8(branchNum);
234  });
236  }
237  else
238  {
239  iterChildren(
240  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
241  s.add8(wireTypeInner);
242  }
243 }
244 
245 void
247 {
248  assert(!isEmpty());
249 
251  iterChildren(
252  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
253 }
254 
255 bool
257 {
258  return isBranch_ == 0;
259 }
260 
261 int
263 {
264  return popcnt16(isBranch_);
265 }
266 
269 {
271  auto hashes = hashesAndChildren_.getHashes();
272  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
273  ret += "\nb";
274  ret += std::to_string(branchNum);
275  ret += " = ";
276  ret += to_string(hashes[indexNum]);
277  });
278  return ret;
279 }
280 
281 // We are modifying an inner node
282 void
284 {
285  assert((m >= 0) && (m < branchFactor));
286  assert(cowid_ != 0);
287  assert(child.get() != this);
288 
289  auto const dstIsBranch = [&] {
290  if (child)
291  return isBranch_ | (1 << m);
292  else
293  return isBranch_ & ~(1 << m);
294  }();
295 
296  auto const dstToAllocate = popcnt16(dstIsBranch);
297  // change hashesAndChildren to remove the element, or make room for the
298  // added element, if necessary
300  std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
301 
302  isBranch_ = dstIsBranch;
303 
304  if (child)
305  {
306  auto const childIndex = *getChildIndex(m);
307  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
308  hashes[childIndex].zero();
309  children[childIndex] = child;
310  }
311 
312  hash_.zero();
313 
315 }
316 
317 // finished modifying, now make shareable
318 void
320 {
321  assert((m >= 0) && (m < branchFactor));
322  assert(cowid_ != 0);
323  assert(child);
324  assert(child.get() != this);
325 
326  assert(!isEmptyBranch(m));
328 }
329 
332 {
333  assert(branch >= 0 && branch < branchFactor);
334  assert(!isEmptyBranch(branch));
335 
337  return hashesAndChildren_.getChildren()[*getChildIndex(branch)].get();
338 }
339 
342 {
343  assert(branch >= 0 && branch < branchFactor);
344  assert(!isEmptyBranch(branch));
345 
347  return hashesAndChildren_.getChildren()[*getChildIndex(branch)];
348 }
349 
350 SHAMapHash const&
352 {
353  assert((m >= 0) && (m < branchFactor));
354  if (auto const i = getChildIndex(m))
355  return hashesAndChildren_.getHashes()[*i];
356 
357  return zeroSHAMapHash;
358 }
359 
362  int branch,
364 {
365  assert(branch >= 0 && branch < branchFactor);
366  assert(node);
367  assert(!isEmptyBranch(branch));
368  auto const childIndex = *getChildIndex(branch);
369  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
370  assert(node->getHash() == hashes[childIndex]);
371 
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  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:196
ripple::SHAMapInnerNode::serializeWithPrefix
void serializeWithPrefix(Serializer &) const override
Serialize the node in a format appropriate for hashing.
Definition: SHAMapInnerNode.cpp:246
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:283
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:80
ripple::SHAMapInnerNode::SHAMapInnerNode
SHAMapInnerNode(std::uint32_t cowid, std::uint8_t numAllocatedChildren=branchFactor)
Definition: SHAMapInnerNode.cpp:45
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:341
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:128
ripple::SHAMapInnerNode::canonicalizeChild
virtual std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:361
ripple::SHAMapTreeNode::cowid_
std::uint32_t cowid_
Determines the owning SHAMap, if any.
Definition: SHAMapTreeNode.h:144
ripple::Serializer::add8
int add8(unsigned char i)
Definition: Serializer.cpp:158
ripple::SHAMapInnerNode::updateHash
void updateHash() override
Recalculate the hash of this node.
Definition: SHAMapInnerNode.cpp:192
iterator
std::lock_guard
STL class.
ripple::SHAMapHash::isZero
bool isZero() const
Definition: SHAMapTreeNode.h:68
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:319
ripple::wireTypeInner
static constexpr unsigned const char wireTypeInner
Definition: SHAMapTreeNode.h:40
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:42
ripple::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:33
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapTreeNode.h:73
ripple::SHAMapTreeNode::getString
virtual std::string getString(SHAMapNodeID const &) const
Definition: SHAMapTreeNode.cpp:187
ripple::SHAMapTreeNode::hash_
SHAMapHash hash_
Definition: SHAMapTreeNode.h:136
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:54
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:47
algorithm
std::tie
T tie(T... args)
ripple::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:194
ripple::SHAMapInnerNode::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(F &&f) const
Call the f callback for all non-empty branches.
Definition: SHAMapInnerNode.cpp:61
ripple::base_uint< 256 >
ripple::SHAMapInnerNode::getString
std::string getString(SHAMapNodeID const &) const override
Definition: SHAMapInnerNode.cpp:268
ripple::HashPrefix::innerNode
@ innerNode
inner node in V1 tree
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:351
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:67
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:133
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:74
array
ripple::Serializer::get8
bool get8(int &, int offset) const
Definition: Serializer.cpp:166
ripple::SHAMapInnerNode::updateHashDeep
void updateHashDeep()
Recalculate the hash of all children and this node.
Definition: SHAMapInnerNode.cpp:207
ripple::SHAMapInnerNode::isBranch_
std::uint16_t isBranch_
Definition: SHAMapInnerNode.h:54
ripple::SHAMapInnerNode::getBranchCount
int getBranchCount() const
Definition: SHAMapInnerNode.cpp:262
std::uint32_t
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:223
ripple::Serializer
Definition: Serializer.h:39
ripple::wireTypeCompressedInner
static constexpr unsigned const char wireTypeCompressedInner
Definition: SHAMapTreeNode.h:41
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:222
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:144
ripple::SHAMapHash::zero
void zero()
Definition: SHAMapTreeNode.h:83
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:237
ripple::hash_append
void hash_append(Hasher &h, Slice const &v)
Definition: Slice.h:195
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::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:256
ripple::SHAMapInnerNode::makeCompressedInner
static std::shared_ptr< SHAMapTreeNode > makeCompressedInner(Slice data)
Definition: SHAMapInnerNode.cpp:159
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:58
ripple::SHAMapInnerNode::getChildPointer
SHAMapTreeNode * getChildPointer(int branch)
Definition: SHAMapInnerNode.cpp:331
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:197
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:53