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