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