rippled
Loading...
Searching...
No Matches
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 <xrpld/shamap/SHAMapInnerNode.h>
21#include <xrpld/shamap/SHAMapTreeNode.h>
22#include <xrpld/shamap/detail/TaggedPointer.ipp>
23
24#include <xrpl/basics/IntrusivePointer.ipp>
25#include <xrpl/basics/Slice.h>
26#include <xrpl/basics/contract.h>
27#include <xrpl/basics/spinlock.h>
28#include <xrpl/protocol/HashPrefix.h>
29#include <xrpl/protocol/digest.h>
30
31namespace ripple {
32
34 std::uint32_t cowid,
35 std::uint8_t numAllocatedChildren)
36 : SHAMapTreeNode(cowid), hashesAndChildren_(numAllocatedChildren)
37{
38}
39
41
42void
44{
46 // structured bindings can't be captured in c++ 17; use tie instead
47 std::tie(std::ignore, std::ignore, children) =
50 [&](auto branchNum, auto indexNum) { children[indexNum].reset(); });
51}
52
53template <class F>
54void
56{
57 hashesAndChildren_.iterChildren(isBranch_, std::forward<F>(f));
58}
59
60template <class F>
61void
63{
65}
66
67void
69{
71 TaggedPointer(std::move(hashesAndChildren_), isBranch_, toAllocate);
72}
73
76{
78}
79
82{
83 auto const branchCount = getBranchCount();
84 auto const thisIsSparse = !hashesAndChildren_.isDense();
85 auto p = intr_ptr::make_shared<SHAMapInnerNode>(cowid, branchCount);
86 p->hash_ = hash_;
87 p->isBranch_ = isBranch_;
88 p->fullBelowGen_ = fullBelowGen_;
89 SHAMapHash *cloneHashes, *thisHashes;
90 intr_ptr::SharedPtr<SHAMapTreeNode>*cloneChildren, *thisChildren;
91 // structured bindings can't be captured in c++ 17; use tie instead
92 std::tie(std::ignore, cloneHashes, cloneChildren) =
93 p->hashesAndChildren_.getHashesAndChildren();
94 std::tie(std::ignore, thisHashes, thisChildren) =
96
97 if (thisIsSparse)
98 {
99 int cloneChildIndex = 0;
100 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
101 cloneHashes[cloneChildIndex++] = thisHashes[indexNum];
102 });
103 }
104 else
105 {
106 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
107 cloneHashes[branchNum] = thisHashes[indexNum];
108 });
109 }
110
111 spinlock sl(lock_);
112 std::lock_guard lock(sl);
113
114 if (thisIsSparse)
115 {
116 int cloneChildIndex = 0;
117 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
118 cloneChildren[cloneChildIndex++] = thisChildren[indexNum];
119 });
120 }
121 else
122 {
123 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
124 cloneChildren[branchNum] = thisChildren[indexNum];
125 });
126 }
127
128 return p;
129}
130
133 Slice data,
134 SHAMapHash const& hash,
135 bool hashValid)
136{
137 // A full inner node is serialized as 16 256-bit hashes, back to back:
138 if (data.size() != branchFactor * uint256::bytes)
139 Throw<std::runtime_error>("Invalid FI node");
140
141 auto ret = intr_ptr::make_shared<SHAMapInnerNode>(0, branchFactor);
142
143 SerialIter si(data);
144
145 auto hashes = ret->hashesAndChildren_.getHashes();
146
147 for (int i = 0; i < branchFactor; ++i)
148 {
149 hashes[i].as_uint256() = si.getBitString<256>();
150
151 if (hashes[i].isNonZero())
152 ret->isBranch_ |= (1 << i);
153 }
154
155 ret->resizeChildArrays(ret->getBranchCount());
156
157 if (hashValid)
158 ret->hash_ = hash;
159 else
160 ret->updateHash();
161
162 return ret;
163}
164
167{
168 // A compressed inner node is serialized as a series of 33 byte chunks,
169 // representing a one byte "position" and a 256-bit hash:
170 constexpr std::size_t chunkSize = uint256::bytes + 1;
171
172 if (auto const s = data.size();
173 (s % chunkSize != 0) || (s > chunkSize * branchFactor))
174 Throw<std::runtime_error>("Invalid CI node");
175
176 SerialIter si(data);
177
178 auto ret = intr_ptr::make_shared<SHAMapInnerNode>(0, branchFactor);
179
180 auto hashes = ret->hashesAndChildren_.getHashes();
181
182 while (!si.empty())
183 {
184 auto const hash = si.getBitString<256>();
185 auto const pos = si.get8();
186
187 if (pos >= branchFactor)
188 Throw<std::runtime_error>("invalid CI node");
189
190 hashes[pos].as_uint256() = hash;
191
192 if (hashes[pos].isNonZero())
193 ret->isBranch_ |= (1 << pos);
194 }
195
196 ret->resizeChildArrays(ret->getBranchCount());
197 ret->updateHash();
198 return ret;
199}
200
201void
203{
204 uint256 nh;
205 if (isBranch_ != 0)
206 {
208 using beast::hash_append;
210 iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
211 nh = static_cast<typename sha512_half_hasher::result_type>(h);
212 }
213 hash_ = SHAMapHash{nh};
214}
215
216void
218{
219 SHAMapHash* hashes;
221 // structured bindings can't be captured in c++ 17; use tie instead
222 std::tie(std::ignore, hashes, children) =
224 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
225 if (auto p = children[indexNum].get())
226 hashes[indexNum] = p->getHash();
227 });
228 updateHash();
229}
230
231void
233{
234 XRPL_ASSERT(
235 !isEmpty(), "ripple::SHAMapInnerNode::serializeForWire : is non-empty");
236
237 // If the node is sparse, then only send non-empty branches:
238 if (getBranchCount() < 12)
239 {
240 // compressed node
241 auto hashes = hashesAndChildren_.getHashes();
242 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
243 s.addBitString(hashes[indexNum].as_uint256());
244 s.add8(branchNum);
245 });
247 }
248 else
249 {
251 [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
253 }
254}
255
256void
258{
259 XRPL_ASSERT(
260 !isEmpty(),
261 "ripple::SHAMapInnerNode::serializeWithPrefix : is non-empty");
262
265 [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
266}
267
270{
272 auto hashes = hashesAndChildren_.getHashes();
273 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
274 ret += "\nb";
275 ret += std::to_string(branchNum);
276 ret += " = ";
277 ret += to_string(hashes[indexNum]);
278 });
279 return ret;
280}
281
282// We are modifying an inner node
283void
285{
286 XRPL_ASSERT(
287 (m >= 0) && (m < branchFactor),
288 "ripple::SHAMapInnerNode::setChild : valid branch input");
289 XRPL_ASSERT(cowid_, "ripple::SHAMapInnerNode::setChild : nonzero cowid");
290 XRPL_ASSERT(
291 child.get() != this,
292 "ripple::SHAMapInnerNode::setChild : valid child input");
293
294 auto const dstIsBranch = [&] {
295 if (child)
296 return isBranch_ | (1u << m);
297 else
298 return isBranch_ & ~(1u << m);
299 }();
300
301 auto const dstToAllocate = popcnt16(dstIsBranch);
302 // change hashesAndChildren to remove the element, or make room for the
303 // added element, if necessary
305 std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
306
307 isBranch_ = dstIsBranch;
308
309 if (child)
310 {
311 auto const childIndex = *getChildIndex(m);
312 auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
313 hashes[childIndex].zero();
314 children[childIndex] = std::move(child);
315 }
316
317 hash_.zero();
318
319 XRPL_ASSERT(
321 "ripple::SHAMapInnerNode::setChild : maximum branch count");
322}
323
324// finished modifying, now make shareable
325void
327 int m,
329{
330 XRPL_ASSERT(
331 (m >= 0) && (m < branchFactor),
332 "ripple::SHAMapInnerNode::shareChild : valid branch input");
333 XRPL_ASSERT(cowid_, "ripple::SHAMapInnerNode::shareChild : nonzero cowid");
334 XRPL_ASSERT(
335 child, "ripple::SHAMapInnerNode::shareChild : non-null child input");
336 XRPL_ASSERT(
337 child.get() != this,
338 "ripple::SHAMapInnerNode::shareChild : valid child input");
339
340 XRPL_ASSERT(
341 !isEmptyBranch(m),
342 "ripple::SHAMapInnerNode::shareChild : non-empty branch input");
344}
345
348{
349 XRPL_ASSERT(
350 branch >= 0 && branch < branchFactor,
351 "ripple::SHAMapInnerNode::getChildPointer : valid branch input");
352 XRPL_ASSERT(
353 !isEmptyBranch(branch),
354 "ripple::SHAMapInnerNode::getChildPointer : non-empty branch input");
355
356 auto const index = *getChildIndex(branch);
357
358 packed_spinlock sl(lock_, index);
359 std::lock_guard lock(sl);
360 return hashesAndChildren_.getChildren()[index].get();
361}
362
365{
366 XRPL_ASSERT(
367 branch >= 0 && branch < branchFactor,
368 "ripple::SHAMapInnerNode::getChild : valid branch input");
369 XRPL_ASSERT(
370 !isEmptyBranch(branch),
371 "ripple::SHAMapInnerNode::getChild : non-empty branch input");
372
373 auto const index = *getChildIndex(branch);
374
375 packed_spinlock sl(lock_, index);
376 std::lock_guard lock(sl);
377 return hashesAndChildren_.getChildren()[index];
378}
379
380SHAMapHash const&
382{
383 XRPL_ASSERT(
384 (m >= 0) && (m < branchFactor),
385 "ripple::SHAMapInnerNode::getChildHash : valid branch input");
386 if (auto const i = getChildIndex(m))
387 return hashesAndChildren_.getHashes()[*i];
388
389 return zeroSHAMapHash;
390}
391
394 int branch,
396{
397 XRPL_ASSERT(
398 branch >= 0 && branch < branchFactor,
399 "ripple::SHAMapInnerNode::canonicalizeChild : valid branch input");
400 XRPL_ASSERT(
401 node != nullptr,
402 "ripple::SHAMapInnerNode::canonicalizeChild : valid node input");
403 XRPL_ASSERT(
404 !isEmptyBranch(branch),
405 "ripple::SHAMapInnerNode::canonicalizeChild : non-empty branch input");
406 auto const childIndex = *getChildIndex(branch);
407 auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
408 XRPL_ASSERT(
409 node->getHash() == hashes[childIndex],
410 "ripple::SHAMapInnerNode::canonicalizeChild : node and branch inputs "
411 "hash do match");
412
413 packed_spinlock sl(lock_, childIndex);
414 std::lock_guard lock(sl);
415
416 if (children[childIndex])
417 {
418 // There is already a node hooked up, return it
419 node = children[childIndex];
420 }
421 else
422 {
423 // Hook this node up
424 children[childIndex] = node;
425 }
426 return node;
427}
428
429void
431{
432 [[maybe_unused]] unsigned count = 0;
433 auto [numAllocated, hashes, children] =
435
436 if (numAllocated != branchFactor)
437 {
438 auto const branchCount = getBranchCount();
439 for (int i = 0; i < branchCount; ++i)
440 {
441 XRPL_ASSERT(
442 hashes[i].isNonZero(),
443 "ripple::SHAMapInnerNode::invariants : nonzero hash in branch");
444 if (children[i] != nullptr)
445 children[i]->invariants();
446 ++count;
447 }
448 }
449 else
450 {
451 for (int i = 0; i < branchFactor; ++i)
452 {
453 if (hashes[i].isNonZero())
454 {
455 XRPL_ASSERT(
456 (isBranch_ & (1 << i)),
457 "ripple::SHAMapInnerNode::invariants : valid branch when "
458 "nonzero hash");
459 if (children[i] != nullptr)
460 children[i]->invariants();
461 ++count;
462 }
463 else
464 {
465 XRPL_ASSERT(
466 (isBranch_ & (1 << i)) == 0,
467 "ripple::SHAMapInnerNode::invariants : valid branch when "
468 "zero hash");
469 }
470 }
471 }
472
473 if (!is_root)
474 {
475 XRPL_ASSERT(
477 "ripple::SHAMapInnerNode::invariants : nonzero hash");
478 XRPL_ASSERT(
479 count >= 1, "ripple::SHAMapInnerNode::invariants : minimum count");
480 }
481 XRPL_ASSERT(
482 (count == 0) ? hash_.isZero() : hash_.isNonZero(),
483 "ripple::SHAMapInnerNode::invariants : hash and count do match");
484}
485
486} // namespace ripple
bool isNonZero() const
Definition: SHAMapHash.h:59
uint256 const & as_uint256() const
Definition: SHAMapHash.h:44
bool isZero() const
Definition: SHAMapHash.h:54
std::uint32_t fullBelowGen_
std::optional< int > getChildIndex(int i) const
Get the child's index inside the hashes or children array (stored in hashesAndChildren_.
SHAMapInnerNode(std::uint32_t cowid, std::uint8_t numAllocatedChildren=2)
void setChild(int m, intr_ptr::SharedPtr< SHAMapTreeNode > child)
static intr_ptr::SharedPtr< SHAMapTreeNode > makeCompressedInner(Slice data)
intr_ptr::SharedPtr< SHAMapTreeNode > clone(std::uint32_t cowid) const override
Make a copy of this node, setting the owner.
static constexpr unsigned int branchFactor
Each inner node has 16 children (the 'radix tree' part of the map)
bool isEmptyBranch(int m) const
void iterNonEmptyChildIndexes(F &&f) const
Call the f callback for all non-empty branches.
void serializeWithPrefix(Serializer &) const override
Serialize the node in a format appropriate for hashing.
void iterChildren(F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
void resizeChildArrays(std::uint8_t toAllocate)
Convert arrays stored in hashesAndChildren_ so they can store the requested number of children.
void updateHash() override
Recalculate the hash of this node.
intr_ptr::SharedPtr< SHAMapTreeNode > getChild(int branch)
void partialDestructor() override
void shareChild(int m, intr_ptr::SharedPtr< SHAMapTreeNode > const &child)
SHAMapHash const & getChildHash(int m) const
void invariants(bool is_root=false) const override
std::string getString(SHAMapNodeID const &) const override
intr_ptr::SharedPtr< SHAMapTreeNode > canonicalizeChild(int branch, intr_ptr::SharedPtr< SHAMapTreeNode > node)
TaggedPointer hashesAndChildren_
Opaque type that contains the hashes array (array of type SHAMapHash) and the children array (array o...
void serializeForWire(Serializer &) const override
Serialize the node in a format appropriate for sending over the wire.
void updateHashDeep()
Recalculate the hash of all children and this node.
SHAMapTreeNode * getChildPointer(int branch)
std::atomic< std::uint16_t > lock_
A bitlock for the children of this node, with one bit per child.
static intr_ptr::SharedPtr< SHAMapTreeNode > makeFullInner(Slice data, SHAMapHash const &hash, bool hashValid)
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:34
virtual std::string getString(SHAMapNodeID const &) const
std::uint32_t cowid_
Determines the owning SHAMap, if any.
base_uint< Bits, Tag > getBitString()
Definition: Serializer.h:460
unsigned char get8()
Definition: Serializer.cpp:353
std::size_t empty() const noexcept
Definition: Serializer.h:368
int addBitString(base_uint< Bits, Tag > const &v)
Definition: Serializer.h:132
int add8(unsigned char i)
Definition: Serializer.cpp:156
A shared intrusive pointer class that supports weak pointers.
void reset()
Set the pointer to null, decrement the strong count, and run the appropriate release action.
An immutable linear range of bytes.
Definition: Slice.h:46
TaggedPointer is a combination of a pointer and a mask stored in the lowest two bits.
Definition: TaggedPointer.h:61
void iterNonEmptyChildIndexes(std::uint16_t isBranch, F &&f) const
Call the f callback for all non-empty branches.
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::tuple< std::uint8_t, SHAMapHash *, intr_ptr::SharedPtr< SHAMapTreeNode > * > getHashesAndChildren() const
Get the number of elements in each array and a pointer to the start of each array.
SHAMapHash * getHashes() const
Get the hashes array.
std::uint8_t capacity() const
Get the number of elements allocated for each array.
void iterChildren(std::uint16_t isBranch, F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
bool isDense() const
Check if the arrays have a dense format.
intr_ptr::SharedPtr< SHAMapTreeNode > * getChildren() const
Get the children array.
static std::size_t constexpr bytes
Definition: base_uint.h:108
Classes to handle arrays of spinlocks packed into a single atomic integer:
Definition: spinlock.h:91
A spinlock implemented on top of an atomic integer.
Definition: spinlock.h:167
std::uint32_t cowid() const
Returns the SHAMap that owns this node.
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
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
void hash_append(Hasher &h, Slice const &v)
Definition: Slice.h:199
static constexpr unsigned char const wireTypeCompressedInner
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:355
static constexpr unsigned char const wireTypeInner
@ innerNode
inner node in V1 tree
int popcnt16(std::uint16_t a)
Returns the SHA512-Half digest of a message.
Definition: digest.h:173
T tie(T... args)
T to_string(T... args)