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