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