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