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 <xrpl/basics/random.h>
21#include <xrpl/shamap/SHAMap.h>
22#include <xrpl/shamap/SHAMapLeafNode.h>
23#include <xrpl/shamap/SHAMapSyncFilter.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 currNodeID;
595 auto currNode = root_.get();
596
597 while (currNode->isInner() &&
598 !static_cast<SHAMapInnerNode*>(currNode)->isFullBelow(generation) &&
599 (currNodeID.getDepth() < node.getDepth()))
600 {
601 int const branch = selectBranch(currNodeID, node.getNodeID());
602 XRPL_ASSERT(branch >= 0, "ripple::SHAMap::addKnownNode : valid branch");
603 auto inner = static_cast<SHAMapInnerNode*>(currNode);
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(currNode, currNodeID) =
618 descend(inner, currNodeID, branch, filter);
619
620 if (currNode != nullptr)
621 continue;
622
623 auto newNode = SHAMapTreeNode::makeFromWire(rawNode);
624
625 if (!newNode || childHash != newNode->getHash())
626 {
627 JLOG(journal_.warn()) << "Corrupt node received";
628 return SHAMapAddNode::invalid();
629 }
630
631 // In rare cases, a node can still be corrupt even after hash
632 // validation. For leaf nodes, we perform an additional check to
633 // ensure the node's position in the tree is consistent with its
634 // content to prevent inconsistencies that could
635 // propagate further down the line.
636 if (newNode->isLeaf())
637 {
638 auto const& actualKey =
639 static_cast<SHAMapLeafNode const*>(newNode.get())
640 ->peekItem()
641 ->key();
642
643 // Validate that this leaf belongs at the target position
644 auto const expectedNodeID =
645 SHAMapNodeID::createID(node.getDepth(), actualKey);
646 if (expectedNodeID.getNodeID() != node.getNodeID())
647 {
648 JLOG(journal_.debug())
649 << "Leaf node position mismatch: "
650 << "expected=" << expectedNodeID.getNodeID()
651 << ", actual=" << node.getNodeID();
652 return SHAMapAddNode::invalid();
653 }
654 }
655
656 // Inner nodes must be at a level strictly less than 64
657 // but leaf nodes (while notionally at level 64) can be
658 // at any depth up to and including 64:
659 if ((currNodeID.getDepth() > leafDepth) ||
660 (newNode->isInner() && currNodeID.getDepth() == leafDepth))
661 {
662 // Map is provably invalid
663 state_ = SHAMapState::Invalid;
664 return SHAMapAddNode::useful();
665 }
666
667 if (currNodeID != node)
668 {
669 // Either this node is broken or we didn't request it (yet)
670 JLOG(journal_.warn()) << "unable to hook node " << node;
671 JLOG(journal_.info()) << " stuck at " << currNodeID;
672 JLOG(journal_.info()) << "got depth=" << node.getDepth()
673 << ", walked to= " << currNodeID.getDepth();
674 return SHAMapAddNode::useful();
675 }
676
677 if (backed_)
678 canonicalize(childHash, newNode);
679
680 newNode = prevNode->canonicalizeChild(branch, std::move(newNode));
681
682 if (filter)
683 {
684 Serializer s;
685 newNode->serializeWithPrefix(s);
686 filter->gotNode(
687 false,
688 childHash,
689 ledgerSeq_,
690 std::move(s.modData()),
691 newNode->getType());
692 }
693
694 return SHAMapAddNode::useful();
695 }
696
697 JLOG(journal_.trace()) << "got node, already had it (late)";
698 return SHAMapAddNode::duplicate();
699}
700
701bool
702SHAMap::deepCompare(SHAMap& other) const
703{
704 // Intended for debug/test only
706
707 stack.push({root_.get(), other.root_.get()});
708
709 while (!stack.empty())
710 {
711 auto const [node, otherNode] = stack.top();
712 stack.pop();
713
714 if (!node || !otherNode)
715 {
716 JLOG(journal_.info()) << "unable to fetch node";
717 return false;
718 }
719 else if (otherNode->getHash() != node->getHash())
720 {
721 JLOG(journal_.warn()) << "node hash mismatch";
722 return false;
723 }
724
725 if (node->isLeaf())
726 {
727 if (!otherNode->isLeaf())
728 return false;
729 auto& nodePeek = static_cast<SHAMapLeafNode*>(node)->peekItem();
730 auto& otherNodePeek =
731 static_cast<SHAMapLeafNode*>(otherNode)->peekItem();
732 if (nodePeek->key() != otherNodePeek->key())
733 return false;
734 if (nodePeek->slice() != otherNodePeek->slice())
735 return false;
736 }
737 else if (node->isInner())
738 {
739 if (!otherNode->isInner())
740 return false;
741 auto node_inner = static_cast<SHAMapInnerNode*>(node);
742 auto other_inner = static_cast<SHAMapInnerNode*>(otherNode);
743 for (int i = 0; i < 16; ++i)
744 {
745 if (node_inner->isEmptyBranch(i))
746 {
747 if (!other_inner->isEmptyBranch(i))
748 return false;
749 }
750 else
751 {
752 if (other_inner->isEmptyBranch(i))
753 return false;
754
755 auto next = descend(node_inner, i);
756 auto otherNext = other.descend(other_inner, i);
757 if (!next || !otherNext)
758 {
759 JLOG(journal_.warn()) << "unable to fetch inner node";
760 return false;
761 }
762 stack.push({next, otherNext});
763 }
764 }
765 }
766 }
767
768 return true;
769}
770
773bool
774SHAMap::hasInnerNode(
775 SHAMapNodeID const& targetNodeID,
776 SHAMapHash const& targetNodeHash) const
777{
778 auto node = root_.get();
779 SHAMapNodeID nodeID;
780
781 while (node->isInner() && (nodeID.getDepth() < targetNodeID.getDepth()))
782 {
783 int branch = selectBranch(nodeID, targetNodeID.getNodeID());
784 auto inner = static_cast<SHAMapInnerNode*>(node);
785 if (inner->isEmptyBranch(branch))
786 return false;
787
788 node = descendThrow(inner, branch);
789 nodeID = nodeID.getChildNodeID(branch);
790 }
791
792 return (node->isInner()) && (node->getHash() == targetNodeHash);
793}
794
797bool
798SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const
799{
800 auto node = root_.get();
801 SHAMapNodeID nodeID;
802
803 if (!node->isInner()) // only one leaf node in the tree
804 return node->getHash() == targetNodeHash;
805
806 do
807 {
808 int branch = selectBranch(nodeID, tag);
809 auto inner = static_cast<SHAMapInnerNode*>(node);
810 if (inner->isEmptyBranch(branch))
811 return false; // Dead end, node must not be here
812
813 if (inner->getChildHash(branch) ==
814 targetNodeHash) // Matching leaf, no need to retrieve it
815 return true;
816
817 node = descendThrow(inner, branch);
818 nodeID = nodeID.getChildNodeID(branch);
819 } while (node->isInner());
820
821 return false; // If this was a matching leaf, we would have caught it
822 // already
823}
824
826SHAMap::getProofPath(uint256 const& key) const
827{
828 SharedPtrNodeStack stack;
829 walkTowardsKey(key, &stack);
830
831 if (stack.empty())
832 {
833 JLOG(journal_.debug()) << "no path to " << key;
834 return {};
835 }
836
837 if (auto const& node = stack.top().first; !node || node->isInner() ||
838 intr_ptr::static_pointer_cast<SHAMapLeafNode>(node)
839 ->peekItem()
840 ->key() != key)
841 {
842 JLOG(journal_.debug()) << "no path to " << key;
843 return {};
844 }
845
847 path.reserve(stack.size());
848 while (!stack.empty())
849 {
850 Serializer s;
851 stack.top().first->serializeForWire(s);
852 path.emplace_back(std::move(s.modData()));
853 stack.pop();
854 }
855
856 JLOG(journal_.debug()) << "getPath for key " << key << ", path length "
857 << path.size();
858 return path;
859}
860
861bool
862SHAMap::verifyProofPath(
863 uint256 const& rootHash,
864 uint256 const& key,
865 std::vector<Blob> const& path)
866{
867 if (path.empty() || path.size() > 65)
868 return false;
869
870 SHAMapHash hash{rootHash};
871 try
872 {
873 for (auto rit = path.rbegin(); rit != path.rend(); ++rit)
874 {
875 auto const& blob = *rit;
876 auto node = SHAMapTreeNode::makeFromWire(makeSlice(blob));
877 if (!node)
878 return false;
879 node->updateHash();
880 if (node->getHash() != hash)
881 return false;
882
883 auto depth = std::distance(path.rbegin(), rit);
884 if (node->isInner())
885 {
886 auto nodeId = SHAMapNodeID::createID(depth, key);
887 hash = static_cast<SHAMapInnerNode*>(node.get())
888 ->getChildHash(selectBranch(nodeId, key));
889 }
890 else
891 {
892 // should exhaust all the blobs now
893 return depth + 1 == path.size();
894 }
895 }
896 }
897 catch (std::exception const&)
898 {
899 // the data in the path may come from the network,
900 // exception could be thrown when parsing the data
901 return false;
902 }
903 return false;
904}
905
906} // 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:97
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
Definition SHAMap.cpp:393
void gmn_ProcessNodes(MissingNodes &, MissingNodes::StackEntry &node)
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:295
intr_ptr::SharedPtr< SHAMapTreeNode > root_
Definition SHAMap.h:108
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition SHAMap.cpp:617
Family & f_
Definition SHAMap.h:99
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:317
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:350
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.
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 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:25
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:244
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:527
std::stack< StackEntry, std::deque< StackEntry > > stack_
Definition SHAMap.h:542
std::condition_variable deferCondVar_
Definition SHAMap.h:553
std::uint32_t generation_
Definition SHAMap.h:523
std::vector< std::pair< SHAMapNodeID, uint256 > > missingNodes_
Definition SHAMap.h:526
std::map< SHAMapInnerNode *, SHAMapNodeID > resumes_
Definition SHAMap.h:558
SHAMapSyncFilter * filter_
Definition SHAMap.h:521
std::vector< DeferredNode > finishedReads_
Definition SHAMap.h:554
T tie(T... args)
T top(T... args)