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 <xrpl/basics/IntrusivePointer.ipp>
21#include <xrpl/basics/Slice.h>
22#include <xrpl/basics/contract.h>
23#include <xrpl/basics/spinlock.h>
24#include <xrpl/protocol/HashPrefix.h>
25#include <xrpl/protocol/digest.h>
26#include <xrpl/shamap/SHAMapInnerNode.h>
27#include <xrpl/shamap/SHAMapTreeNode.h>
28#include <xrpl/shamap/detail/TaggedPointer.ipp>
29
30namespace ripple {
31
33 std::uint32_t cowid,
34 std::uint8_t numAllocatedChildren)
35 : SHAMapTreeNode(cowid), hashesAndChildren_(numAllocatedChildren)
36{
37}
38
40
41void
43{
45 // structured bindings can't be captured in c++ 17; use tie instead
46 std::tie(std::ignore, std::ignore, children) =
49 [&](auto branchNum, auto indexNum) { children[indexNum].reset(); });
50}
51
52template <class F>
53void
58
59template <class F>
60void
65
66void
72
78
81{
82 auto const branchCount = getBranchCount();
83 auto const thisIsSparse = !hashesAndChildren_.isDense();
84 auto p = intr_ptr::make_shared<SHAMapInnerNode>(cowid, branchCount);
85 p->hash_ = hash_;
86 p->isBranch_ = isBranch_;
87 p->fullBelowGen_ = fullBelowGen_;
88 SHAMapHash *cloneHashes, *thisHashes;
89 intr_ptr::SharedPtr<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 }
109
110 spinlock sl(lock_);
111 std::lock_guard lock(sl);
112
113 if (thisIsSparse)
114 {
115 int cloneChildIndex = 0;
116 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
117 cloneChildren[cloneChildIndex++] = thisChildren[indexNum];
118 });
119 }
120 else
121 {
122 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
123 cloneChildren[branchNum] = thisChildren[indexNum];
124 });
125 }
126
127 return p;
128}
129
132 Slice data,
133 SHAMapHash const& hash,
134 bool hashValid)
135{
136 // A full inner node is serialized as 16 256-bit hashes, back to back:
137 if (data.size() != branchFactor * uint256::bytes)
138 Throw<std::runtime_error>("Invalid FI node");
139
140 auto ret = intr_ptr::make_shared<SHAMapInnerNode>(0, branchFactor);
141
142 SerialIter si(data);
143
144 auto hashes = ret->hashesAndChildren_.getHashes();
145
146 for (int i = 0; i < branchFactor; ++i)
147 {
148 hashes[i].as_uint256() = si.getBitString<256>();
149
150 if (hashes[i].isNonZero())
151 ret->isBranch_ |= (1 << i);
152 }
153
154 ret->resizeChildArrays(ret->getBranchCount());
155
156 if (hashValid)
157 ret->hash_ = hash;
158 else
159 ret->updateHash();
160
161 return ret;
162}
163
166{
167 // A compressed inner node is serialized as a series of 33 byte chunks,
168 // representing a one byte "position" and a 256-bit hash:
169 constexpr std::size_t chunkSize = uint256::bytes + 1;
170
171 if (auto const s = data.size();
172 (s % chunkSize != 0) || (s > chunkSize * branchFactor))
173 Throw<std::runtime_error>("Invalid CI node");
174
175 SerialIter si(data);
176
177 auto ret = intr_ptr::make_shared<SHAMapInnerNode>(0, branchFactor);
178
179 auto hashes = ret->hashesAndChildren_.getHashes();
180
181 while (!si.empty())
182 {
183 auto const hash = si.getBitString<256>();
184 auto const pos = si.get8();
185
186 if (pos >= branchFactor)
187 Throw<std::runtime_error>("invalid CI node");
188
189 hashes[pos].as_uint256() = hash;
190
191 if (hashes[pos].isNonZero())
192 ret->isBranch_ |= (1 << pos);
193 }
194
195 ret->resizeChildArrays(ret->getBranchCount());
196 ret->updateHash();
197 return ret;
198}
199
200void
202{
203 uint256 nh;
204 if (isBranch_ != 0)
205 {
207 using beast::hash_append;
209 iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
210 nh = static_cast<typename sha512_half_hasher::result_type>(h);
211 }
212 hash_ = SHAMapHash{nh};
213}
214
215void
217{
218 SHAMapHash* hashes;
220 // structured bindings can't be captured in c++ 17; use tie instead
221 std::tie(std::ignore, hashes, children) =
223 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
224 if (auto p = children[indexNum].get())
225 hashes[indexNum] = p->getHash();
226 });
227 updateHash();
228}
229
230void
232{
233 XRPL_ASSERT(
234 !isEmpty(), "ripple::SHAMapInnerNode::serializeForWire : is non-empty");
235
236 // If the node is sparse, then only send non-empty branches:
237 if (getBranchCount() < 12)
238 {
239 // compressed node
240 auto hashes = hashesAndChildren_.getHashes();
241 iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
242 s.addBitString(hashes[indexNum].as_uint256());
243 s.add8(branchNum);
244 });
246 }
247 else
248 {
250 [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
252 }
253}
254
255void
257{
258 XRPL_ASSERT(
259 !isEmpty(),
260 "ripple::SHAMapInnerNode::serializeWithPrefix : is non-empty");
261
264 [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
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
282void
284{
285 XRPL_ASSERT(
286 (m >= 0) && (m < branchFactor),
287 "ripple::SHAMapInnerNode::setChild : valid branch input");
288 XRPL_ASSERT(cowid_, "ripple::SHAMapInnerNode::setChild : nonzero cowid");
289 XRPL_ASSERT(
290 child.get() != this,
291 "ripple::SHAMapInnerNode::setChild : valid child input");
292
293 auto const dstIsBranch = [&] {
294 if (child)
295 return isBranch_ | (1u << m);
296 else
297 return isBranch_ & ~(1u << 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
318 XRPL_ASSERT(
320 "ripple::SHAMapInnerNode::setChild : maximum branch count");
321}
322
323// finished modifying, now make shareable
324void
326 int m,
328{
329 XRPL_ASSERT(
330 (m >= 0) && (m < branchFactor),
331 "ripple::SHAMapInnerNode::shareChild : valid branch input");
332 XRPL_ASSERT(cowid_, "ripple::SHAMapInnerNode::shareChild : nonzero cowid");
333 XRPL_ASSERT(
334 child, "ripple::SHAMapInnerNode::shareChild : non-null child input");
335 XRPL_ASSERT(
336 child.get() != this,
337 "ripple::SHAMapInnerNode::shareChild : valid child input");
338
339 XRPL_ASSERT(
340 !isEmptyBranch(m),
341 "ripple::SHAMapInnerNode::shareChild : non-empty branch input");
343}
344
347{
348 XRPL_ASSERT(
349 branch >= 0 && branch < branchFactor,
350 "ripple::SHAMapInnerNode::getChildPointer : valid branch input");
351 XRPL_ASSERT(
352 !isEmptyBranch(branch),
353 "ripple::SHAMapInnerNode::getChildPointer : non-empty branch input");
354
355 auto const index = *getChildIndex(branch);
356
357 packed_spinlock sl(lock_, index);
358 std::lock_guard lock(sl);
359 return hashesAndChildren_.getChildren()[index].get();
360}
361
364{
365 XRPL_ASSERT(
366 branch >= 0 && branch < branchFactor,
367 "ripple::SHAMapInnerNode::getChild : valid branch input");
368 XRPL_ASSERT(
369 !isEmptyBranch(branch),
370 "ripple::SHAMapInnerNode::getChild : non-empty branch input");
371
372 auto const index = *getChildIndex(branch);
373
374 packed_spinlock sl(lock_, index);
375 std::lock_guard lock(sl);
376 return hashesAndChildren_.getChildren()[index];
377}
378
379SHAMapHash const&
381{
382 XRPL_ASSERT(
383 (m >= 0) && (m < branchFactor),
384 "ripple::SHAMapInnerNode::getChildHash : valid branch input");
385 if (auto const i = getChildIndex(m))
386 return hashesAndChildren_.getHashes()[*i];
387
388 return zeroSHAMapHash;
389}
390
393 int branch,
395{
396 XRPL_ASSERT(
397 branch >= 0 && branch < branchFactor,
398 "ripple::SHAMapInnerNode::canonicalizeChild : valid branch input");
399 XRPL_ASSERT(
400 node != nullptr,
401 "ripple::SHAMapInnerNode::canonicalizeChild : valid node input");
402 XRPL_ASSERT(
403 !isEmptyBranch(branch),
404 "ripple::SHAMapInnerNode::canonicalizeChild : non-empty branch input");
405 auto const childIndex = *getChildIndex(branch);
406 auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
407 XRPL_ASSERT(
408 node->getHash() == hashes[childIndex],
409 "ripple::SHAMapInnerNode::canonicalizeChild : node and branch inputs "
410 "hash do match");
411
412 packed_spinlock sl(lock_, childIndex);
413 std::lock_guard lock(sl);
414
415 if (children[childIndex])
416 {
417 // There is already a node hooked up, return it
418 node = children[childIndex];
419 }
420 else
421 {
422 // Hook this node up
423 children[childIndex] = node;
424 }
425 return node;
426}
427
428void
430{
431 [[maybe_unused]] unsigned count = 0;
432 auto [numAllocated, hashes, children] =
434
435 if (numAllocated != branchFactor)
436 {
437 auto const branchCount = getBranchCount();
438 for (int i = 0; i < branchCount; ++i)
439 {
440 XRPL_ASSERT(
441 hashes[i].isNonZero(),
442 "ripple::SHAMapInnerNode::invariants : nonzero hash in branch");
443 if (children[i] != nullptr)
444 children[i]->invariants();
445 ++count;
446 }
447 }
448 else
449 {
450 for (int i = 0; i < branchFactor; ++i)
451 {
452 if (hashes[i].isNonZero())
453 {
454 XRPL_ASSERT(
455 (isBranch_ & (1 << i)),
456 "ripple::SHAMapInnerNode::invariants : valid branch when "
457 "nonzero hash");
458 if (children[i] != nullptr)
459 children[i]->invariants();
460 ++count;
461 }
462 else
463 {
464 XRPL_ASSERT(
465 (isBranch_ & (1 << i)) == 0,
466 "ripple::SHAMapInnerNode::invariants : valid branch when "
467 "zero hash");
468 }
469 }
470 }
471
472 if (!is_root)
473 {
474 XRPL_ASSERT(
476 "ripple::SHAMapInnerNode::invariants : nonzero hash");
477 XRPL_ASSERT(
478 count >= 1, "ripple::SHAMapInnerNode::invariants : minimum count");
479 }
480 XRPL_ASSERT(
481 (count == 0) ? hash_.isZero() : hash_.isNonZero(),
482 "ripple::SHAMapInnerNode::invariants : hash and count do match");
483}
484
485} // 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::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.
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:459
unsigned char get8()
std::size_t empty() const noexcept
Definition Serializer.h:367
int addBitString(base_uint< Bits, Tag > const &v)
Definition Serializer.h:131
int add8(unsigned char i)
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.
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.
T is_same_v
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.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
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.
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:172
T tie(T... args)
T to_string(T... args)