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