xrpld
Loading...
Searching...
No Matches
libxrpl/shamap/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* map,
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 ((map != nullptr) && (root_->getHash() == map->root_->getHash()))
122 return;
123
124 if (root_->isLeaf())
125 {
127 if ((map == nullptr) || !map->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 auto const childID = nodeID.getChildNodeID(i);
153 auto next = descendThrow(node, i);
154
155 if (next->isInner())
156 {
157 if ((map == nullptr) || !map->hasInnerNode(childID, childHash))
158 stack.emplace(safeDowncast<SHAMapInnerNode*>(next), childID);
159 }
160 else if ((map == nullptr) || !map->hasLeafNode(leafKey(*next), 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 const branch = (firstChild + currentChild++) % 16;
186 if (node->isEmptyBranch(branch))
187 continue;
188
189 auto const& childHash = node->getChildHash(branch);
190
191 if (mn.missingHashes.contains(childHash))
192 {
193 // we already know this child node is missing
194 fullBelow = false;
195 }
196 else if (!backed_ || !f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
197 {
198 bool pending = false;
199 auto d = descendAsync(
200 node,
201 branch,
202 mn.filter,
203 pending,
204 [node, nodeID, branch, &mn](SHAMapTreeNodePtr found, SHAMapHash const&) {
205 // a read completed asynchronously
206 std::unique_lock<std::mutex> const lock{mn.deferLock};
207 mn.finishedReads.emplace_back(node, nodeID, branch, std::move(found));
209 });
210
211 if (pending)
212 {
213 fullBelow = false;
214 ++mn.deferred;
215 }
216 else if (d == nullptr)
217 {
218 // node is not in database
219
220 fullBelow = false; // for now, not known full below
221 mn.missingHashes.insert(childHash);
222 mn.missingNodes.emplace_back(nodeID.getChildNodeID(branch), childHash.asUInt256());
223
224 if (--mn.max <= 0)
225 return;
226 }
227 else if (d->isInner() && !safeDowncast<SHAMapInnerNode*>(d)->isFullBelow(mn.generation))
228 {
229 mn.stack.push(se);
230
231 // Switch to processing the child node
233 nodeID = nodeID.getChildNodeID(branch);
234 firstChild = randInt(255);
235 currentChild = 0;
236 fullBelow = true;
237 }
238 }
239 }
240
241 // We have finished processing an inner node
242 // and thus (for now) all its children
243
244 if (fullBelow)
245 { // No partial node encountered below this node
246 node->setFullBelowGen(mn.generation);
247 if (backed_)
248 {
249 f_.getFullBelowCache()->insert(node->getHash().asUInt256());
250 }
251 }
252
253 node = nullptr;
254}
255
256// Wait for deferred reads to finish and
257// process their results
258void
260{
261 // Process all deferred reads
262 int complete = 0;
263 while (complete != mn.deferred)
264 {
266 {
268
269 while (mn.finishedReads.size() <= complete)
270 mn.deferCondVar.wait(lock);
271 deferredNode = std::move(mn.finishedReads[complete++]);
272 }
273
274 auto parent = std::get<0>(deferredNode);
275 auto const& parentID = std::get<1>(deferredNode);
276 auto branch = std::get<2>(deferredNode);
277 auto nodePtr = std::get<3>(deferredNode);
278 auto const& nodeHash = parent->getChildHash(branch);
279
280 if (nodePtr)
281 { // Got the node
282 nodePtr = parent->canonicalizeChild(branch, std::move(nodePtr));
283
284 // When we finish this stack, we need to restart
285 // with the parent of this node
286 mn.resumes[parent] = parentID;
287 }
288 else if ((mn.max > 0) && (mn.missingHashes.insert(nodeHash).second))
289 {
290 mn.missingNodes.emplace_back(parentID.getChildNodeID(branch), nodeHash.asUInt256());
291 --mn.max;
292 }
293 }
294
295 mn.finishedReads.clear();
296 mn.finishedReads.reserve(mn.maxDefer);
297 mn.deferred = 0;
298}
299
307{
308 XRPL_ASSERT(root_->getHash().isNonZero(), "xrpl::SHAMap::getMissingNodes : nonzero root hash");
309 XRPL_ASSERT(max > 0, "xrpl::SHAMap::getMissingNodes : valid max input");
310
311 MissingNodes mn(
312 max,
313 filter,
314 512, // number of async reads per pass
315 f_.getFullBelowCache()->getGeneration());
316
317 if (!root_->isInner() ||
319 {
321 return std::move(mn.missingNodes);
322 }
323
324 // Start at the root.
325 // The firstChild value is selected randomly so if multiple threads
326 // are traversing the map, each thread will start at a different
327 // (randomly selected) inner node. This increases the likelihood
328 // that the two threads will produce different request sets (which is
329 // more efficient than sending identical requests).
332 auto& node = std::get<0>(pos);
333 auto& nextChild = std::get<3>(pos);
334 auto& fullBelow = std::get<4>(pos);
335
336 // Traverse the map without blocking
337 do
338 {
339 while ((node != nullptr) && (mn.deferred <= mn.maxDefer))
340 {
341 gmnProcessNodes(mn, pos);
342
343 if (mn.max <= 0)
344 break;
345
346 if ((node == nullptr) && !mn.stack.empty())
347 {
348 // Pick up where we left off with this node's parent
349 bool const was = fullBelow; // was full below
350
351 pos = mn.stack.top();
352 mn.stack.pop();
353 if (nextChild == 0)
354 {
355 // This is a node we are processing for the first time
356 fullBelow = true;
357 }
358 else
359 {
360 // This is a node we are continuing to process
361 fullBelow = fullBelow && was; // was and still is
362 }
363 XRPL_ASSERT(node, "xrpl::SHAMap::getMissingNodes : first non-null node");
364 }
365 }
366
367 // We have either emptied the stack or
368 // posted as many deferred reads as we can
369 if (mn.deferred != 0)
371
372 if (mn.max <= 0)
373 return std::move(mn.missingNodes);
374
375 if (node == nullptr)
376 { // We weren't in the middle of processing a node
377
378 if (mn.stack.empty() && !mn.resumes.empty())
379 {
380 // Recheck nodes we could not finish before
381 for (auto const& [innerNode, nodeId] : mn.resumes)
382 {
383 if (!innerNode->isFullBelow(mn.generation))
384 mn.stack.emplace(innerNode, nodeId, randInt(255), 0, true);
385 }
386
387 mn.resumes.clear();
388 }
389
390 if (!mn.stack.empty())
391 {
392 // Resume at the top of the stack
393 pos = mn.stack.top();
394 mn.stack.pop();
395 XRPL_ASSERT(node, "xrpl::SHAMap::getMissingNodes : second non-null node");
396 }
397 }
398
399 // node will only still be nullptr if
400 // we finished the current node, the stack is empty
401 // and we have no nodes to resume
402
403 } while (node != nullptr);
404
405 if (mn.missingNodes.empty())
407
408 return std::move(mn.missingNodes);
409}
410
411bool
413 SHAMapNodeID const& wanted,
415 bool fatLeaves,
416 std::uint32_t depth) const
417{
418 // Gets a node and some of its children
419 // to a specified depth
420
421 auto node = root_.get();
422 SHAMapNodeID nodeID;
423
424 while ((node != nullptr) && node->isInner() && (nodeID.getDepth() < wanted.getDepth()))
425 {
426 int const branch = selectBranch(nodeID, wanted.getNodeID());
427 auto inner = safeDowncast<SHAMapInnerNode*>(node);
428 if (inner->isEmptyBranch(branch))
429 return false;
430 node = descendThrow(inner, branch);
431 nodeID = nodeID.getChildNodeID(branch);
432 }
433
434 if (node == nullptr || wanted != nodeID)
435 {
436 JLOG(journal_.info()) << "peer requested node that is not in the map: " << wanted
437 << " but found " << nodeID;
438 return false;
439 }
440
441 if (node->isInner() && safeDowncast<SHAMapInnerNode*>(node)->isEmpty())
442 {
443 JLOG(journal_.warn()) << "peer requests empty node";
444 return false;
445 }
446
448 stack.emplace(node, nodeID, depth);
449
450 Serializer s(8192);
451
452 while (!stack.empty())
453 {
454 std::tie(node, nodeID, depth) = stack.top();
455 stack.pop();
456
457 // Add this node to the reply
458 s.erase();
459 node->serializeForWire(s);
460 data.emplace_back(nodeID, node->isLeaf(), s.getData());
461
462 if (node->isInner())
463 {
464 // We descend inner nodes with only a single child
465 // without decrementing the depth
466 auto inner = safeDowncast<SHAMapInnerNode*>(node);
467 int const bc = inner->getBranchCount();
468
469 if ((depth > 0) || (bc == 1))
470 {
471 // We need to process this node's children
472 for (int i = 0; i < 16; ++i)
473 {
474 if (!inner->isEmptyBranch(i))
475 {
476 auto const childNode = descendThrow(inner, i);
477 auto const childID = nodeID.getChildNodeID(i);
478
479 if (childNode->isInner() && ((depth > 1) || (bc == 1)))
480 {
481 // If there's more than one child, reduce the depth
482 // If only one child, follow the chain
483 stack.emplace(childNode, childID, (bc > 1) ? (depth - 1) : depth);
484 }
485 else if (childNode->isInner() || fatLeaves)
486 {
487 // Just include this node
488 s.erase();
489 childNode->serializeForWire(s);
490 data.emplace_back(childID, childNode->isLeaf(), s.getData());
491 }
492 }
493 }
494 }
495 }
496 }
497
498 return true;
499}
500
501void
503{
504 root_->serializeForWire(s);
505}
506
509 SHAMapHash const& hash,
510 SHAMapTreeNodePtr rootNode,
511 SHAMapSyncFilter const* filter)
512{
513 XRPL_ASSERT(cowid_ >= 1, "xrpl::SHAMap::addRootNode : valid cowid");
514 XRPL_ASSERT(rootNode, "xrpl::SHAMap::addRootNode : non-null root node");
515
516 // we already have a root_ node
517 if (root_->getHash().isNonZero())
518 {
519 JLOG(journal_.trace()) << "Got root node, already have one";
520 XRPL_ASSERT(root_->getHash() == hash, "xrpl::SHAMap::addRootNode : valid hash");
522 }
523
524 if (rootNode->getHash() != hash)
525 {
526 JLOG(journal_.warn()) << "Corrupt root node received: expected hash " << hash << ", got "
527 << rootNode->getHash();
528 return SHAMapAddNode::invalid();
529 }
530
531 if (backed_)
532 canonicalize(hash, rootNode);
533
534 root_ = std::move(rootNode);
535
536 if (root_->isLeaf())
538
539 if (filter != nullptr)
540 {
541 Serializer s;
542 root_->serializeWithPrefix(s);
543 filter->gotNode(
544 false, root_->getHash(), ledgerSeq_, std::move(s.modData()), root_->getType());
545 }
546
547 return SHAMapAddNode::useful();
548}
549
552 SHAMapNodeID const& nodeID,
553 SHAMapTreeNodePtr treeNode,
554 SHAMapSyncFilter const* filter)
555{
556 XRPL_ASSERT(!nodeID.isRoot(), "xrpl::SHAMap::addKnownNode : valid node");
557 XRPL_ASSERT(treeNode, "xrpl::SHAMap::addKnownNode : non-null tree node");
558 XRPL_ASSERT(
559 !treeNode->isLeaf() ||
560 SHAMapNodeID::createID(nodeID.getDepth(), leafKey(*treeNode)).getNodeID() ==
561 nodeID.getNodeID(),
562 "xrpl::SHAMap::addKnownNode : leaf position consistent with node ID");
563
564 if (!isSynching())
565 {
566 JLOG(journal_.trace()) << "AddKnownNode while not synching";
568 }
569
570 auto const generation = f_.getFullBelowCache()->getGeneration();
571 SHAMapNodeID currNodeID;
572 auto currNode = root_.get();
573
574 while (currNode->isInner() &&
575 !safeDowncast<SHAMapInnerNode*>(currNode)->isFullBelow(generation) &&
576 (currNodeID.getDepth() < nodeID.getDepth()))
577 {
578 int const branch = selectBranch(currNodeID, nodeID.getNodeID());
579 XRPL_ASSERT(branch >= 0, "xrpl::SHAMap::addKnownNode : valid branch");
580 auto inner = safeDowncast<SHAMapInnerNode*>(currNode);
581 if (inner->isEmptyBranch(branch))
582 {
583 JLOG(journal_.warn()) << "Add known node " << nodeID << " for empty branch " << branch
584 << " at " << currNodeID;
585 return SHAMapAddNode::invalid();
586 }
587
588 auto childHash = inner->getChildHash(branch);
589 if (f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
590 {
592 }
593
594 auto prevNode = inner;
595 std::tie(currNode, currNodeID) = descend(inner, currNodeID, branch, filter);
596
597 if (currNode != nullptr)
598 continue;
599
600 if (childHash != treeNode->getHash())
601 {
602 JLOG(journal_.warn()) << "Corrupt node " << nodeID << " received: expected hash "
603 << childHash << ", got " << treeNode->getHash();
604 return SHAMapAddNode::invalid();
605 }
606
607 // Inner nodes must be at a level strictly less than 64
608 // but leaf nodes (while notionally at level 64) can be
609 // at any depth up to and including 64:
610 if ((currNodeID.getDepth() > kLeafDepth) ||
611 (treeNode->isInner() && currNodeID.getDepth() == kLeafDepth))
612 {
613 // Map is provably invalid
615 return SHAMapAddNode::useful();
616 }
617
618 if (currNodeID != nodeID)
619 {
620 // Either this node is broken or we didn't request it (yet)
621 JLOG(journal_.warn()) << "unable to hook node " << nodeID;
622 JLOG(journal_.info()) << " stuck at " << currNodeID;
623 JLOG(journal_.info()) << "got depth=" << nodeID.getDepth()
624 << ", walked to= " << currNodeID.getDepth();
625 return SHAMapAddNode::useful();
626 }
627
628 if (backed_)
629 canonicalize(childHash, treeNode);
630
631 treeNode = prevNode->canonicalizeChild(branch, std::move(treeNode));
632
633 if (filter != nullptr)
634 {
635 Serializer s;
636 treeNode->serializeWithPrefix(s);
637 filter->gotNode(
638 false, childHash, ledgerSeq_, std::move(s.modData()), treeNode->getType());
639 }
640
641 return SHAMapAddNode::useful();
642 }
643
644 JLOG(journal_.trace()) << "got node, already had it (late)";
646}
647
648bool
650{
651 // Intended for debug/test only
653
654 stack.emplace(root_.get(), other.root_.get());
655
656 while (!stack.empty())
657 {
658 auto const [node, otherNode] = stack.top();
659 stack.pop();
660
661 if ((node == nullptr) || (otherNode == nullptr))
662 {
663 JLOG(journal_.info()) << "unable to fetch node";
664 return false;
665 }
666 if (otherNode->getHash() != node->getHash())
667 {
668 JLOG(journal_.warn()) << "node hash mismatch";
669 return false;
670 }
671
672 if (node->isLeaf())
673 {
674 if (!otherNode->isLeaf())
675 return false;
676 auto& nodePeek = safeDowncast<SHAMapLeafNode*>(node)->peekItem();
677 auto& otherNodePeek = safeDowncast<SHAMapLeafNode*>(otherNode)->peekItem();
678 if (nodePeek->key() != otherNodePeek->key())
679 return false;
680 if (nodePeek->slice() != otherNodePeek->slice())
681 return false;
682 }
683 else if (node->isInner())
684 {
685 if (!otherNode->isInner())
686 return false;
687 auto nodeInner = safeDowncast<SHAMapInnerNode*>(node);
688 auto otherInner = safeDowncast<SHAMapInnerNode*>(otherNode);
689 for (int i = 0; i < 16; ++i)
690 {
691 if (nodeInner->isEmptyBranch(i))
692 {
693 if (!otherInner->isEmptyBranch(i))
694 return false;
695 }
696 else
697 {
698 if (otherInner->isEmptyBranch(i))
699 return false;
700
701 auto next = descend(nodeInner, i);
702 auto otherNext = other.descend(otherInner, i);
703 if ((next == nullptr) || (otherNext == nullptr))
704 {
705 JLOG(journal_.warn()) << "unable to fetch inner node";
706 return false;
707 }
708 stack.emplace(next, otherNext);
709 }
710 }
711 }
712 }
713
714 return true;
715}
716
720bool
721SHAMap::hasInnerNode(SHAMapNodeID const& targetNodeID, SHAMapHash const& targetNodeHash) const
722{
723 auto node = root_.get();
724 SHAMapNodeID nodeID;
725
726 while (node->isInner() && (nodeID.getDepth() < targetNodeID.getDepth()))
727 {
728 int const branch = selectBranch(nodeID, targetNodeID.getNodeID());
729 auto inner = safeDowncast<SHAMapInnerNode*>(node);
730 if (inner->isEmptyBranch(branch))
731 return false;
732
733 node = descendThrow(inner, branch);
734 nodeID = nodeID.getChildNodeID(branch);
735 }
736
737 return (node->isInner()) && (node->getHash() == targetNodeHash);
738}
739
743bool
744SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const
745{
746 auto node = root_.get();
747 SHAMapNodeID nodeID;
748
749 if (!node->isInner()) // only one leaf node in the tree
750 return node->getHash() == targetNodeHash;
751
752 do
753 {
754 int const branch = selectBranch(nodeID, tag);
755 auto inner = safeDowncast<SHAMapInnerNode*>(node);
756 if (inner->isEmptyBranch(branch))
757 return false; // Dead end, node must not be here
758
759 if (inner->getChildHash(branch) == targetNodeHash) // Matching leaf, no need to retrieve it
760 return true;
761
762 node = descendThrow(inner, branch);
763 nodeID = nodeID.getChildNodeID(branch);
764 } while (node->isInner());
765
766 return false; // If this was a matching leaf, we would have caught it
767 // already
768}
769
772{
773 SharedPtrNodeStack stack;
774 walkTowardsKey(key, &stack);
775
776 if (stack.empty())
777 {
778 JLOG(journal_.debug()) << "no path to " << key;
779 return {};
780 }
781
782 if (auto const& node = stack.top().first; !node || node->isInner() ||
783 intr_ptr::staticPointerCast<SHAMapLeafNode>(node)->peekItem()->key() != key)
784 {
785 JLOG(journal_.debug()) << "no path to " << key;
786 return {};
787 }
788
790 path.reserve(stack.size());
791 while (!stack.empty())
792 {
793 Serializer s;
794 stack.top().first->serializeForWire(s);
795 path.emplace_back(std::move(s.modData()));
796 stack.pop();
797 }
798
799 JLOG(journal_.debug()) << "getPath for key " << key << ", path length " << path.size();
800 return path;
801}
802
803bool
805{
806 if (path.empty() || path.size() > 65)
807 return false;
808
809 SHAMapHash hash{rootHash};
810 try
811 {
812 for (auto rit = path.rbegin(); rit != path.rend(); ++rit)
813 {
814 auto const& blob = *rit;
815 auto node = SHAMapTreeNode::makeFromWire(makeSlice(blob));
816 if (!node)
817 return false;
818 node->updateHash();
819 if (node->getHash() != hash)
820 return false;
821
822 auto depth = std::distance(path.rbegin(), rit);
823 if (node->isInner())
824 {
825 auto nodeId = SHAMapNodeID::createID(depth, key);
826 hash = safeDowncast<SHAMapInnerNode*>(node.get())
827 ->getChildHash(selectBranch(nodeId, key));
828 }
829 else
830 {
831 // should exhaust all the blobs now
832 return depth + 1 == path.size();
833 }
834 }
835 }
836 catch (std::exception const&)
837 {
838 // the data in the path may come from the network,
839 // exception could be thrown when parsing the data
840 return false;
841 }
842 return false;
843}
844
845} // 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
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
static bool verifyProofPath(uint256 const &rootHash, uint256 const &key, std::vector< Blob > const &path)
Verify the proof path.
SHAMapState state_
Definition SHAMap.h:129
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:121
static constexpr unsigned int kLeafDepth
The depth of the hash map: data is only present in the leaves.
Definition SHAMap.h:144
Family & f_
Definition SHAMap.h:115
bool getNodeFat(SHAMapNodeID const &wanted, std::vector< SHAMapNodeData > &data, bool fatLeaves, std::uint32_t depth) const
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
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:654
bool deepCompare(SHAMap &other) const
void gmnProcessNodes(MissingNodes &, MissingNodes::StackEntry &node)
beast::Journal journal_
Definition SHAMap.h:116
SHAMapAddNode addRootNode(SHAMapHash const &hash, SHAMapTreeNodePtr rootNode, SHAMapSyncFilter const *filter)
Add a root node to the SHAMap during synchronization.
void serializeRoot(Serializer &s) const
Serializes the root in a format appropriate for sending over the wire.
bool backed_
Definition SHAMap.h:131
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
SHAMap()=delete
std::stack< std::pair< SHAMapTreeNodePtr, SHAMapNodeID > > SharedPtrNodeStack
Definition SHAMap.h:423
SHAMapAddNode addKnownNode(SHAMapNodeID const &nodeID, SHAMapTreeNodePtr treeNode, SHAMapSyncFilter const *filter)
Add a known node at a specific position in the SHAMap during synchronization.
void canonicalize(SHAMapHash const &hash, SHAMapTreeNodePtr &) const
bool hasLeafNode(uint256 const &tag, SHAMapHash const &hash) const
Does this map have this leaf node?
void clearSynching()
Definition SHAMap.h:666
SHAMapTreeNodePtr descendNoStore(SHAMapInnerNode &, int branch) const
SHAMapTreeNodePtr root_
Definition SHAMap.h:128
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
Definition SHAMap.h:126
void visitNodes(std::function< bool(SHAMapTreeNode &)> const &function) const
Visit every node in this SHAMap.
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Blob getData() const
Definition Serializer.h:182
T * get() const
Get the raw pointer.
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:84
Integral randInt(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
uint256 const & leafKey(SHAMapTreeNode const &node)
Return the key of the item held by a SHAMap leaf node.
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
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:70
BaseUInt< 256 > uint256
Definition base_uint.h:580
T pop(T... args)
T size(T... args)
std::set< SHAMapHash > missingHashes
Definition SHAMap.h:578
std::vector< DeferredNode > finishedReads
Definition SHAMap.h:605
std::vector< std::pair< SHAMapNodeID, uint256 > > missingNodes
Definition SHAMap.h:577
std::uint32_t generation
Definition SHAMap.h:574
SHAMapSyncFilter const * filter
Definition SHAMap.h:572
std::map< SHAMapInnerNode *, SHAMapNodeID > resumes
Definition SHAMap.h:609
std::tuple< SHAMapInnerNode *, SHAMapNodeID, int, int, bool > StackEntry
Definition SHAMap.h:581
std::stack< StackEntry, std::deque< StackEntry > > stack
Definition SHAMap.h:593
std::condition_variable deferCondVar
Definition SHAMap.h:604
T tie(T... args)
T top(T... args)