rippled
Loading...
Searching...
No Matches
SHAMapSync.cpp
1#include <xrpl/basics/random.h>
2#include <xrpl/basics/safe_cast.h>
3#include <xrpl/shamap/SHAMap.h>
4#include <xrpl/shamap/SHAMapLeafNode.h>
5#include <xrpl/shamap/SHAMapSyncFilter.h>
6
7namespace xrpl {
8
9void
11 std::function<void(boost::intrusive_ptr<SHAMapItem const> const& item)> const& leafFunction)
12 const
13{
14 visitNodes([&leafFunction](SHAMapTreeNode& node) {
15 if (!node.isInner())
16 leafFunction(safe_downcast<SHAMapLeafNode&>(node).peekItem());
17 return true;
18 });
19}
20
21void
22SHAMap::visitNodes(std::function<bool(SHAMapTreeNode&)> const& function) const
23{
24 if (!root_)
25 return;
26
27 function(*root_);
28
29 if (!root_->isInner())
30 return;
31
34
35 auto node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(root_);
36 int pos = 0;
37
38 while (true)
39 {
40 while (pos < 16)
41 {
42 if (!node->isEmptyBranch(pos))
43 {
45 if (!function(*child))
46 return;
47
48 if (child->isLeaf())
49 {
50 ++pos;
51 }
52 else
53 {
54 // If there are no more children, don't push this node
55 while ((pos != 15) && (node->isEmptyBranch(pos + 1)))
56 ++pos;
57
58 if (pos != 15)
59 {
60 // save next position to resume at
61 stack.push(std::make_pair(pos + 1, std::move(node)));
62 }
63
64 // descend to the child's first position
65 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(child);
66 pos = 0;
67 }
68 }
69 else
70 {
71 ++pos; // move to next position
72 }
73 }
74
75 if (stack.empty())
76 break;
77
78 std::tie(pos, node) = stack.top();
79 stack.pop();
80 }
81}
82
83void
85 SHAMap const* have,
86 std::function<bool(SHAMapTreeNode const&)> const& function) const
87{
88 // Visit every node in this SHAMap that is not present
89 // in the specified SHAMap
90 if (!root_)
91 return;
92
93 if (root_->getHash().isZero())
94 return;
95
96 if ((have != nullptr) && (root_->getHash() == have->root_->getHash()))
97 return;
98
99 if (root_->isLeaf())
100 {
101 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(root_);
102 if ((have == nullptr) || !have->hasLeafNode(leaf->peekItem()->key(), leaf->getHash()))
103 function(*root_);
104 return;
105 }
106 // contains unexplored non-matching inner node entries
109
110 stack.push({safe_downcast<SHAMapInnerNode*>(root_.get()), SHAMapNodeID{}});
111
112 while (!stack.empty())
113 {
114 auto const [node, nodeID] = stack.top();
115 stack.pop();
116
117 // 1) Add this node to the pack
118 if (!function(*node))
119 return;
120
121 // 2) push non-matching child inner nodes
122 for (int i = 0; i < 16; ++i)
123 {
124 if (!node->isEmptyBranch(i))
125 {
126 auto const& childHash = node->getChildHash(i);
127 SHAMapNodeID const childID = nodeID.getChildNodeID(i);
128 auto next = descendThrow(node, i);
129
130 if (next->isInner())
131 {
132 if ((have == nullptr) || !have->hasInnerNode(childID, childHash))
133 stack.push({safe_downcast<SHAMapInnerNode*>(next), childID});
134 }
135 else if (
136 (have == nullptr) ||
137 !have->hasLeafNode(
138 safe_downcast<SHAMapLeafNode*>(next)->peekItem()->key(), childHash))
139 {
140 if (!function(*next))
141 return;
142 }
143 }
144 }
145 }
146}
147
148// Starting at the position referred to by the specfied
149// StackEntry, process that node and its first resident
150// children, descending the SHAMap until we complete the
151// processing of a node.
152void
154{
155 SHAMapInnerNode*& node = std::get<0>(se);
156 SHAMapNodeID& nodeID = std::get<1>(se);
157 int& firstChild = std::get<2>(se);
158 int& currentChild = std::get<3>(se);
159 bool& fullBelow = std::get<4>(se);
160
161 while (currentChild < 16)
162 {
163 int const branch = (firstChild + currentChild++) % 16;
164 if (node->isEmptyBranch(branch))
165 continue;
166
167 auto const& childHash = node->getChildHash(branch);
168
169 if (mn.missingHashes_.contains(childHash))
170 {
171 // we already know this child node is missing
172 fullBelow = false;
173 }
174 else if (!backed_ || !f_.getFullBelowCache()->touch_if_exists(childHash.as_uint256()))
175 {
176 bool pending = false;
177 auto d = descendAsync(
178 node,
179 branch,
180 mn.filter_,
181 pending,
182 [node, nodeID, branch, &mn](
184 // a read completed asynchronously
185 std::unique_lock<std::mutex> const lock{mn.deferLock_};
186 mn.finishedReads_.emplace_back(node, nodeID, branch, std::move(found));
188 });
189
190 if (pending)
191 {
192 fullBelow = false;
193 ++mn.deferred_;
194 }
195 else if (d == nullptr)
196 {
197 // node is not in database
198
199 fullBelow = false; // for now, not known full below
200 mn.missingHashes_.insert(childHash);
201 mn.missingNodes_.emplace_back(
202 nodeID.getChildNodeID(branch), childHash.as_uint256());
203
204 if (--mn.max_ <= 0)
205 return;
206 }
207 else if (
208 d->isInner() && !safe_downcast<SHAMapInnerNode*>(d)->isFullBelow(mn.generation_))
209 {
210 mn.stack_.push(se);
211
212 // Switch to processing the child node
213 node = safe_downcast<SHAMapInnerNode*>(d);
214 nodeID = nodeID.getChildNodeID(branch);
215 firstChild = rand_int(255);
216 currentChild = 0;
217 fullBelow = true;
218 }
219 }
220 }
221
222 // We have finished processing an inner node
223 // and thus (for now) all its children
224
225 if (fullBelow)
226 { // No partial node encountered below this node
227 node->setFullBelowGen(mn.generation_);
228 if (backed_)
229 {
230 f_.getFullBelowCache()->insert(node->getHash().as_uint256());
231 }
232 }
233
234 node = nullptr;
235}
236
237// Wait for deferred reads to finish and
238// process their results
239void
240SHAMap::gmn_ProcessDeferredReads(MissingNodes& mn)
241{
242 // Process all deferred reads
243 int complete = 0;
244 while (complete != mn.deferred_)
245 {
247 deferredNode;
248 {
250
251 while (mn.finishedReads_.size() <= complete)
252 mn.deferCondVar_.wait(lock);
253 deferredNode = std::move(mn.finishedReads_[complete++]);
254 }
255
256 auto parent = std::get<0>(deferredNode);
257 auto const& parentID = std::get<1>(deferredNode);
258 auto branch = std::get<2>(deferredNode);
259 auto nodePtr = std::get<3>(deferredNode);
260 auto const& nodeHash = parent->getChildHash(branch);
261
262 if (nodePtr)
263 { // Got the node
264 nodePtr = parent->canonicalizeChild(branch, std::move(nodePtr));
265
266 // When we finish this stack, we need to restart
267 // with the parent of this node
268 mn.resumes_[parent] = parentID;
269 }
270 else if ((mn.max_ > 0) && (mn.missingHashes_.insert(nodeHash).second))
271 {
272 mn.missingNodes_.emplace_back(parentID.getChildNodeID(branch), nodeHash.as_uint256());
273 --mn.max_;
274 }
275 }
276
277 mn.finishedReads_.clear();
278 mn.finishedReads_.reserve(mn.maxDefer_);
279 mn.deferred_ = 0;
280}
281
287SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter)
288{
289 XRPL_ASSERT(root_->getHash().isNonZero(), "xrpl::SHAMap::getMissingNodes : nonzero root hash");
290 XRPL_ASSERT(max > 0, "xrpl::SHAMap::getMissingNodes : valid max input");
291
292 MissingNodes mn(
293 max,
294 filter,
295 512, // number of async reads per pass
296 f_.getFullBelowCache()->getGeneration());
297
298 if (!root_->isInner() ||
299 intr_ptr::static_pointer_cast<SHAMapInnerNode>(root_)->isFullBelow(mn.generation_))
300 {
301 clearSynching();
302 return std::move(mn.missingNodes_);
303 }
304
305 // Start at the root.
306 // The firstChild value is selected randomly so if multiple threads
307 // are traversing the map, each thread will start at a different
308 // (randomly selected) inner node. This increases the likelihood
309 // that the two threads will produce different request sets (which is
310 // more efficient than sending identical requests).
312 safe_downcast<SHAMapInnerNode*>(root_.get()), SHAMapNodeID(), rand_int(255), 0, true};
313 auto& node = std::get<0>(pos);
314 auto& nextChild = std::get<3>(pos);
315 auto& fullBelow = std::get<4>(pos);
316
317 // Traverse the map without blocking
318 do
319 {
320 while ((node != nullptr) && (mn.deferred_ <= mn.maxDefer_))
321 {
322 gmn_ProcessNodes(mn, pos);
323
324 if (mn.max_ <= 0)
325 break;
326
327 if ((node == nullptr) && !mn.stack_.empty())
328 {
329 // Pick up where we left off with this node's parent
330 bool const was = fullBelow; // was full below
331
332 pos = mn.stack_.top();
333 mn.stack_.pop();
334 if (nextChild == 0)
335 {
336 // This is a node we are processing for the first time
337 fullBelow = true;
338 }
339 else
340 {
341 // This is a node we are continuing to process
342 fullBelow = fullBelow && was; // was and still is
343 }
344 XRPL_ASSERT(node, "xrpl::SHAMap::getMissingNodes : first non-null node");
345 }
346 }
347
348 // We have either emptied the stack or
349 // posted as many deferred reads as we can
350 if (mn.deferred_ != 0)
351 gmn_ProcessDeferredReads(mn);
352
353 if (mn.max_ <= 0)
354 return std::move(mn.missingNodes_);
355
356 if (node == nullptr)
357 { // We weren't in the middle of processing a node
358
359 if (mn.stack_.empty() && !mn.resumes_.empty())
360 {
361 // Recheck nodes we could not finish before
362 for (auto const& [innerNode, nodeId] : mn.resumes_)
363 {
364 if (!innerNode->isFullBelow(mn.generation_))
365 mn.stack_.push(std::make_tuple(innerNode, nodeId, rand_int(255), 0, true));
366 }
367
368 mn.resumes_.clear();
369 }
370
371 if (!mn.stack_.empty())
372 {
373 // Resume at the top of the stack
374 pos = mn.stack_.top();
375 mn.stack_.pop();
376 XRPL_ASSERT(node, "xrpl::SHAMap::getMissingNodes : second non-null node");
377 }
378 }
379
380 // node will only still be nullptr if
381 // we finished the current node, the stack is empty
382 // and we have no nodes to resume
383
384 } while (node != nullptr);
385
386 if (mn.missingNodes_.empty())
387 clearSynching();
388
389 return std::move(mn.missingNodes_);
390}
391
392bool
393SHAMap::getNodeFat(
394 SHAMapNodeID const& wanted,
396 bool fatLeaves,
397 std::uint32_t depth) const
398{
399 // Gets a node and some of its children
400 // to a specified depth
401
402 auto node = root_.get();
403 SHAMapNodeID nodeID;
404
405 while ((node != nullptr) && node->isInner() && (nodeID.getDepth() < wanted.getDepth()))
406 {
407 int const branch = selectBranch(nodeID, wanted.getNodeID());
408 auto inner = safe_downcast<SHAMapInnerNode*>(node);
409 if (inner->isEmptyBranch(branch))
410 return false;
411 node = descendThrow(inner, branch);
412 nodeID = nodeID.getChildNodeID(branch);
413 }
414
415 if (node == nullptr || wanted != nodeID)
416 {
417 JLOG(journal_.info()) << "peer requested node that is not in the map: " << wanted
418 << " but found " << nodeID;
419 return false;
420 }
421
422 if (node->isInner() && safe_downcast<SHAMapInnerNode*>(node)->isEmpty())
423 {
424 JLOG(journal_.warn()) << "peer requests empty node";
425 return false;
426 }
427
429 stack.emplace(node, nodeID, depth);
430
431 Serializer s(8192);
432
433 while (!stack.empty())
434 {
435 std::tie(node, nodeID, depth) = stack.top();
436 stack.pop();
437
438 // Add this node to the reply
439 s.erase();
440 node->serializeForWire(s);
441 data.emplace_back(std::make_pair(nodeID, s.getData()));
442
443 if (node->isInner())
444 {
445 // We descend inner nodes with only a single child
446 // without decrementing the depth
447 auto inner = safe_downcast<SHAMapInnerNode*>(node);
448 int const bc = inner->getBranchCount();
449
450 if ((depth > 0) || (bc == 1))
451 {
452 // We need to process this node's children
453 for (int i = 0; i < 16; ++i)
454 {
455 if (!inner->isEmptyBranch(i))
456 {
457 auto const childNode = descendThrow(inner, i);
458 auto const childID = nodeID.getChildNodeID(i);
459
460 if (childNode->isInner() && ((depth > 1) || (bc == 1)))
461 {
462 // If there's more than one child, reduce the depth
463 // If only one child, follow the chain
464 stack.emplace(childNode, childID, (bc > 1) ? (depth - 1) : depth);
465 }
466 else if (childNode->isInner() || fatLeaves)
467 {
468 // Just include this node
469 s.erase();
470 childNode->serializeForWire(s);
471 data.emplace_back(std::make_pair(childID, s.getData()));
472 }
473 }
474 }
475 }
476 }
477 }
478
479 return true;
480}
481
482void
483SHAMap::serializeRoot(Serializer& s) const
484{
485 root_->serializeForWire(s);
486}
487
489SHAMap::addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFilter* filter)
490{
491 // we already have a root_ node
492 if (root_->getHash().isNonZero())
493 {
494 JLOG(journal_.trace()) << "got root node, already have one";
495 XRPL_ASSERT(root_->getHash() == hash, "xrpl::SHAMap::addRootNode : valid hash input");
496 return SHAMapAddNode::duplicate();
497 }
498
499 XRPL_ASSERT(cowid_ >= 1, "xrpl::SHAMap::addRootNode : valid cowid");
500 auto node = SHAMapTreeNode::makeFromWire(rootNode);
501 if (!node || node->getHash() != hash)
502 return SHAMapAddNode::invalid();
503
504 if (backed_)
505 canonicalize(hash, node);
506
507 root_ = node;
508
509 if (root_->isLeaf())
510 clearSynching();
511
512 if (filter != nullptr)
513 {
514 Serializer s;
515 root_->serializeWithPrefix(s);
516 filter->gotNode(
517 false, root_->getHash(), ledgerSeq_, std::move(s.modData()), root_->getType());
518 }
519
520 return SHAMapAddNode::useful();
521}
522
524SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncFilter* filter)
525{
526 XRPL_ASSERT(!node.isRoot(), "xrpl::SHAMap::addKnownNode : valid node input");
527
528 if (!isSynching())
529 {
530 JLOG(journal_.trace()) << "AddKnownNode while not synching";
531 return SHAMapAddNode::duplicate();
532 }
533
534 auto const generation = f_.getFullBelowCache()->getGeneration();
535 SHAMapNodeID currNodeID;
536 auto currNode = root_.get();
537
538 while (currNode->isInner() &&
539 !safe_downcast<SHAMapInnerNode*>(currNode)->isFullBelow(generation) &&
540 (currNodeID.getDepth() < node.getDepth()))
541 {
542 int const branch = selectBranch(currNodeID, node.getNodeID());
543 XRPL_ASSERT(branch >= 0, "xrpl::SHAMap::addKnownNode : valid branch");
544 auto inner = safe_downcast<SHAMapInnerNode*>(currNode);
545 if (inner->isEmptyBranch(branch))
546 {
547 JLOG(journal_.warn()) << "Add known node for empty branch" << node;
548 return SHAMapAddNode::invalid();
549 }
550
551 auto childHash = inner->getChildHash(branch);
552 if (f_.getFullBelowCache()->touch_if_exists(childHash.as_uint256()))
553 {
554 return SHAMapAddNode::duplicate();
555 }
556
557 auto prevNode = inner;
558 std::tie(currNode, currNodeID) = descend(inner, currNodeID, branch, filter);
559
560 if (currNode != nullptr)
561 continue;
562
563 auto newNode = SHAMapTreeNode::makeFromWire(rawNode);
564
565 if (!newNode || childHash != newNode->getHash())
566 {
567 JLOG(journal_.warn()) << "Corrupt node received";
568 return SHAMapAddNode::invalid();
569 }
570
571 // In rare cases, a node can still be corrupt even after hash
572 // validation. For leaf nodes, we perform an additional check to
573 // ensure the node's position in the tree is consistent with its
574 // content to prevent inconsistencies that could
575 // propagate further down the line.
576 if (newNode->isLeaf())
577 {
578 auto const& actualKey =
579 safe_downcast<SHAMapLeafNode const*>(newNode.get())->peekItem()->key();
580
581 // Validate that this leaf belongs at the target position
582 auto const expectedNodeID = SHAMapNodeID::createID(node.getDepth(), actualKey);
583 if (expectedNodeID.getNodeID() != node.getNodeID())
584 {
585 JLOG(journal_.debug())
586 << "Leaf node position mismatch: "
587 << "expected=" << expectedNodeID.getNodeID() << ", actual=" << node.getNodeID();
588 return SHAMapAddNode::invalid();
589 }
590 }
591
592 // Inner nodes must be at a level strictly less than 64
593 // but leaf nodes (while notionally at level 64) can be
594 // at any depth up to and including 64:
595 if ((currNodeID.getDepth() > leafDepth) ||
596 (newNode->isInner() && currNodeID.getDepth() == leafDepth))
597 {
598 // Map is provably invalid
599 state_ = SHAMapState::Invalid;
600 return SHAMapAddNode::useful();
601 }
602
603 if (currNodeID != node)
604 {
605 // Either this node is broken or we didn't request it (yet)
606 JLOG(journal_.warn()) << "unable to hook node " << node;
607 JLOG(journal_.info()) << " stuck at " << currNodeID;
608 JLOG(journal_.info()) << "got depth=" << node.getDepth()
609 << ", walked to= " << currNodeID.getDepth();
610 return SHAMapAddNode::useful();
611 }
612
613 if (backed_)
614 canonicalize(childHash, newNode);
615
616 newNode = prevNode->canonicalizeChild(branch, std::move(newNode));
617
618 if (filter != nullptr)
619 {
620 Serializer s;
621 newNode->serializeWithPrefix(s);
622 filter->gotNode(
623 false, childHash, ledgerSeq_, std::move(s.modData()), newNode->getType());
624 }
625
626 return SHAMapAddNode::useful();
627 }
628
629 JLOG(journal_.trace()) << "got node, already had it (late)";
630 return SHAMapAddNode::duplicate();
631}
632
633bool
634SHAMap::deepCompare(SHAMap& other) const
635{
636 // Intended for debug/test only
638
639 stack.push({root_.get(), other.root_.get()});
640
641 while (!stack.empty())
642 {
643 auto const [node, otherNode] = stack.top();
644 stack.pop();
645
646 if ((node == nullptr) || (otherNode == nullptr))
647 {
648 JLOG(journal_.info()) << "unable to fetch node";
649 return false;
650 }
651 if (otherNode->getHash() != node->getHash())
652 {
653 JLOG(journal_.warn()) << "node hash mismatch";
654 return false;
655 }
656
657 if (node->isLeaf())
658 {
659 if (!otherNode->isLeaf())
660 return false;
661 auto& nodePeek = safe_downcast<SHAMapLeafNode*>(node)->peekItem();
662 auto& otherNodePeek = safe_downcast<SHAMapLeafNode*>(otherNode)->peekItem();
663 if (nodePeek->key() != otherNodePeek->key())
664 return false;
665 if (nodePeek->slice() != otherNodePeek->slice())
666 return false;
667 }
668 else if (node->isInner())
669 {
670 if (!otherNode->isInner())
671 return false;
672 auto node_inner = safe_downcast<SHAMapInnerNode*>(node);
673 auto other_inner = safe_downcast<SHAMapInnerNode*>(otherNode);
674 for (int i = 0; i < 16; ++i)
675 {
676 if (node_inner->isEmptyBranch(i))
677 {
678 if (!other_inner->isEmptyBranch(i))
679 return false;
680 }
681 else
682 {
683 if (other_inner->isEmptyBranch(i))
684 return false;
685
686 auto next = descend(node_inner, i);
687 auto otherNext = other.descend(other_inner, i);
688 if ((next == nullptr) || (otherNext == nullptr))
689 {
690 JLOG(journal_.warn()) << "unable to fetch inner node";
691 return false;
692 }
693 stack.push({next, otherNext});
694 }
695 }
696 }
697 }
698
699 return true;
700}
701
704bool
705SHAMap::hasInnerNode(SHAMapNodeID const& targetNodeID, SHAMapHash const& targetNodeHash) const
706{
707 auto node = root_.get();
708 SHAMapNodeID nodeID;
709
710 while (node->isInner() && (nodeID.getDepth() < targetNodeID.getDepth()))
711 {
712 int const branch = selectBranch(nodeID, targetNodeID.getNodeID());
713 auto inner = safe_downcast<SHAMapInnerNode*>(node);
714 if (inner->isEmptyBranch(branch))
715 return false;
716
717 node = descendThrow(inner, branch);
718 nodeID = nodeID.getChildNodeID(branch);
719 }
720
721 return (node->isInner()) && (node->getHash() == targetNodeHash);
722}
723
726bool
727SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const
728{
729 auto node = root_.get();
730 SHAMapNodeID nodeID;
731
732 if (!node->isInner()) // only one leaf node in the tree
733 return node->getHash() == targetNodeHash;
734
735 do
736 {
737 int const branch = selectBranch(nodeID, tag);
738 auto inner = safe_downcast<SHAMapInnerNode*>(node);
739 if (inner->isEmptyBranch(branch))
740 return false; // Dead end, node must not be here
741
742 if (inner->getChildHash(branch) == targetNodeHash) // Matching leaf, no need to retrieve it
743 return true;
744
745 node = descendThrow(inner, branch);
746 nodeID = nodeID.getChildNodeID(branch);
747 } while (node->isInner());
748
749 return false; // If this was a matching leaf, we would have caught it
750 // already
751}
752
754SHAMap::getProofPath(uint256 const& key) const
755{
756 SharedPtrNodeStack stack;
757 walkTowardsKey(key, &stack);
758
759 if (stack.empty())
760 {
761 JLOG(journal_.debug()) << "no path to " << key;
762 return {};
763 }
764
765 if (auto const& node = stack.top().first; !node || node->isInner() ||
766 intr_ptr::static_pointer_cast<SHAMapLeafNode>(node)->peekItem()->key() != key)
767 {
768 JLOG(journal_.debug()) << "no path to " << key;
769 return {};
770 }
771
773 path.reserve(stack.size());
774 while (!stack.empty())
775 {
776 Serializer s;
777 stack.top().first->serializeForWire(s);
778 path.emplace_back(std::move(s.modData()));
779 stack.pop();
780 }
781
782 JLOG(journal_.debug()) << "getPath for key " << key << ", path length " << path.size();
783 return path;
784}
785
786bool
787SHAMap::verifyProofPath(uint256 const& rootHash, uint256 const& key, std::vector<Blob> const& path)
788{
789 if (path.empty() || path.size() > 65)
790 return false;
791
792 SHAMapHash hash{rootHash};
793 try
794 {
795 for (auto rit = path.rbegin(); rit != path.rend(); ++rit)
796 {
797 auto const& blob = *rit;
798 auto node = SHAMapTreeNode::makeFromWire(makeSlice(blob));
799 if (!node)
800 return false;
801 node->updateHash();
802 if (node->getHash() != hash)
803 return false;
804
805 auto depth = std::distance(path.rbegin(), rit);
806 if (node->isInner())
807 {
808 auto nodeId = SHAMapNodeID::createID(depth, key);
809 hash = safe_downcast<SHAMapInnerNode*>(node.get())
810 ->getChildHash(selectBranch(nodeId, key));
811 }
812 else
813 {
814 // should exhaust all the blobs now
815 return depth + 1 == path.size();
816 }
817 }
818 }
819 catch (std::exception const&)
820 {
821 // the data in the path may come from the network,
822 // exception could be thrown when parsing the data
823 return false;
824 }
825 return false;
826}
827
828} // namespace xrpl
virtual std::shared_ptr< FullBelowCache > getFullBelowCache()=0
Return a pointer to the Family Full Below Cache.
SHAMapHash const & getChildHash(int m) const
bool isEmptyBranch(int m) const
Identifies a node inside a SHAMap.
uint256 const & getNodeID() const
SHAMapNodeID getChildNodeID(unsigned int m) const
unsigned int getDepth() const
bool isRoot() const
virtual void gotNode(bool fromFilter, SHAMapHash const &nodeHash, std::uint32_t ledgerSeq, Blob &&nodeData, SHAMapNodeType type) const =0
virtual bool isInner() const =0
Determines if this is an inner node.
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition SHAMap.h:77
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:276
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
Definition SHAMap.cpp:347
Family & f_
Definition SHAMap.h:79
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition SHAMap.cpp:556
void visitDifferences(SHAMap const *have, std::function< bool(SHAMapTreeNode const &)> const &) const
Visit every node in this SHAMap that is not present in the specified SHAMap.
bool hasInnerNode(SHAMapNodeID const &nodeID, SHAMapHash const &hash) const
Does this map have this inner node?
void gmn_ProcessNodes(MissingNodes &, MissingNodes::StackEntry &node)
bool backed_
Definition SHAMap.h:91
void visitLeaves(std::function< void(boost::intrusive_ptr< SHAMapItem const > const &)> const &) const
Visit every leaf node in this SHAMap.
bool hasLeafNode(uint256 const &tag, SHAMapHash const &hash) const
Does this map have this leaf node?
intr_ptr::SharedPtr< SHAMapTreeNode > descendNoStore(SHAMapInnerNode &, int branch) const
Definition SHAMap.cpp:308
void visitNodes(std::function< bool(SHAMapTreeNode &)> const &function) const
Visit every node in this SHAMap.
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:254
intr_ptr::SharedPtr< SHAMapTreeNode > root_
Definition SHAMap.h:88
Blob getData() const
Definition Serializer.h:181
A shared intrusive pointer class that supports weak pointers.
An immutable linear range of bytes.
Definition Slice.h:26
T distance(T... args)
T emplace_back(T... args)
T emplace(T... args)
T empty(T... args)
T is_same_v
T make_pair(T... args)
T make_tuple(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ pending
List will be valid in the future.
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
unsigned int selectBranch(SHAMapNodeID const &id, uint256 const &hash)
Returns the branch that would contain the given hash.
@ innerNode
inner node in V1 tree
std::enable_if_t< std::is_integral< Integral >::value &&detail::is_engine< Engine >::value, Integral > rand_int(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
T pop(T... args)
T push(T... args)
T rbegin(T... args)
T rend(T... args)
T reserve(T... args)
T size(T... args)
std::condition_variable deferCondVar_
Definition SHAMap.h:505
std::map< SHAMapInnerNode *, SHAMapNodeID > resumes_
Definition SHAMap.h:510
std::vector< DeferredNode > finishedReads_
Definition SHAMap.h:506
std::set< SHAMapHash > missingHashes_
Definition SHAMap.h:479
std::vector< std::pair< SHAMapNodeID, uint256 > > missingNodes_
Definition SHAMap.h:478
std::stack< StackEntry, std::deque< StackEntry > > stack_
Definition SHAMap.h:494
SHAMapSyncFilter * filter_
Definition SHAMap.h:473
std::uint32_t generation_
Definition SHAMap.h:475
T tie(T... args)
T top(T... args)