20#include <xrpld/shamap/SHAMap.h>
21#include <xrpld/shamap/SHAMapSyncFilter.h>
23#include <xrpl/basics/random.h>
29 std::function<
void(boost::intrusive_ptr<SHAMapItem const>
const&
30 item)>
const& leafFunction)
const
47 if (!
root_->isInner())
53 auto node = std::static_pointer_cast<SHAMapInnerNode>(
root_);
60 if (!node->isEmptyBranch(pos))
64 if (!function(*child))
72 while ((pos != 15) && (node->isEmptyBranch(pos + 1)))
82 node = std::static_pointer_cast<SHAMapInnerNode>(child);
110 if (
root_->getHash().isZero())
113 if (have && (
root_->getHash() == have->
root_->getHash()))
118 auto leaf = std::static_pointer_cast<SHAMapLeafNode>(
root_);
120 !have->
hasLeafNode(leaf->peekItem()->key(), leaf->getHash()))
130 while (!stack.
empty())
132 auto const [node, nodeID] = stack.
top();
136 if (!function(*node))
140 for (
int i = 0; i < 16; ++i)
142 if (!node->isEmptyBranch(i))
144 auto const& childHash = node->getChildHash(i);
160 if (!function(*next))
177 int& firstChild = std::get<2>(se);
178 int& currentChild = std::get<3>(se);
179 bool& fullBelow = std::get<4>(se);
181 while (currentChild < 16)
183 int branch = (firstChild + currentChild++) % 16;
204 [node, nodeID, branch, &mn](
207 std::unique_lock<std::mutex> lock{mn.deferLock_};
209 node, nodeID, branch, std::move(found));
251 node->setFullBelowGen(mn.generation_);
254 f_.getFullBelowCache()->insert(node->getHash().as_uint256());
284 auto parent = std::get<0>(deferredNode);
285 auto const& parentID = std::get<1>(deferredNode);
286 auto branch = std::get<2>(deferredNode);
287 auto nodePtr = std::get<3>(deferredNode);
288 auto const& nodeHash = parent->getChildHash(branch);
292 nodePtr = parent->canonicalizeChild(branch, std::move(nodePtr));
301 parentID.getChildNodeID(branch), nodeHash.as_uint256());
319 root_->getHash().isNonZero(),
320 "ripple::SHAMap::getMissingNodes : nonzero root hash");
321 XRPL_ASSERT(max > 0,
"ripple::SHAMap::getMissingNodes : valid max input");
327 f_.getFullBelowCache()->getGeneration());
329 if (!root_->isInner() ||
330 std::static_pointer_cast<SHAMapInnerNode>(root_)->isFullBelow(
349 auto& node = std::get<0>(pos);
350 auto& nextChild = std::get<3>(pos);
351 auto& fullBelow = std::get<4>(pos);
358 gmn_ProcessNodes(mn, pos);
363 if ((node ==
nullptr) && !mn.
stack_.empty())
366 bool was = fullBelow;
378 fullBelow = fullBelow && was;
382 "ripple::SHAMap::getMissingNodes : first non-null node");
389 gmn_ProcessDeferredReads(mn);
415 "ripple::SHAMap::getMissingNodes : second non-null node");
423 }
while (node !=
nullptr);
441 auto node = root_.get();
448 if (inner->isEmptyBranch(branch))
450 node = descendThrow(inner, branch);
454 if (node ==
nullptr || wanted != nodeID)
456 JLOG(journal_.info())
457 <<
"peer requested node that is not in the map: " << wanted
458 <<
" but found " << nodeID;
464 JLOG(journal_.warn()) <<
"peer requests empty node";
469 stack.
emplace(node, nodeID, depth);
473 while (!stack.
empty())
480 node->serializeForWire(s);
490 if ((depth > 0) || (bc == 1))
493 for (
int i = 0; i < 16; ++i)
495 if (!inner->isEmptyBranch(i))
497 auto const childNode = descendThrow(inner, i);
500 if (childNode->isInner() && ((depth > 1) || (bc == 1)))
507 (bc > 1) ? (depth - 1) : depth);
509 else if (childNode->isInner() || fatLeaves)
513 childNode->serializeForWire(s);
529 root_->serializeForWire(s);
535 Slice const& rootNode,
539 if (root_->getHash().isNonZero())
541 JLOG(journal_.trace()) <<
"got root node, already have one";
543 root_->getHash() == hash,
544 "ripple::SHAMap::addRootNode : valid hash input");
545 return SHAMapAddNode::duplicate();
548 XRPL_ASSERT(cowid_ >= 1,
"ripple::SHAMap::addRootNode : valid cowid");
549 auto node = SHAMapTreeNode::makeFromWire(rootNode);
550 if (!node || node->getHash() != hash)
551 return SHAMapAddNode::invalid();
554 canonicalize(hash, node);
564 root_->serializeWithPrefix(s);
573 return SHAMapAddNode::useful();
579 Slice const& rawNode,
583 !node.
isRoot(),
"ripple::SHAMap::addKnownNode : valid node input");
587 JLOG(journal_.trace()) <<
"AddKnownNode while not synching";
588 return SHAMapAddNode::duplicate();
591 auto const generation = f_.getFullBelowCache()->getGeneration();
593 auto iNode = root_.get();
595 while (iNode->isInner() &&
600 XRPL_ASSERT(branch >= 0,
"ripple::SHAMap::addKnownNode : valid branch");
602 if (inner->isEmptyBranch(branch))
604 JLOG(journal_.warn()) <<
"Add known node for empty branch" << node;
605 return SHAMapAddNode::invalid();
608 auto childHash = inner->getChildHash(branch);
609 if (f_.getFullBelowCache()->touch_if_exists(childHash.as_uint256()))
611 return SHAMapAddNode::duplicate();
614 auto prevNode = inner;
615 std::tie(iNode, iNodeID) = descend(inner, iNodeID, branch, filter);
617 if (iNode ==
nullptr)
619 auto newNode = SHAMapTreeNode::makeFromWire(rawNode);
621 if (!newNode || childHash != newNode->getHash())
623 JLOG(journal_.warn()) <<
"Corrupt node received";
624 return SHAMapAddNode::invalid();
630 if ((iNodeID.
getDepth() > leafDepth) ||
631 (newNode->isInner() && iNodeID.
getDepth() == leafDepth))
634 state_ = SHAMapState::Invalid;
635 return SHAMapAddNode::useful();
641 JLOG(journal_.warn()) <<
"unable to hook node " << node;
642 JLOG(journal_.info()) <<
" stuck at " << iNodeID;
643 JLOG(journal_.info()) <<
"got depth=" << node.
getDepth()
644 <<
", walked to= " << iNodeID.
getDepth();
645 return SHAMapAddNode::useful();
649 canonicalize(childHash, newNode);
651 newNode = prevNode->canonicalizeChild(branch, std::move(newNode));
656 newNode->serializeWithPrefix(s);
665 return SHAMapAddNode::useful();
669 JLOG(journal_.trace()) <<
"got node, already had it (late)";
670 return SHAMapAddNode::duplicate();
679 stack.
push({root_.get(), other.
root_.get()});
681 while (!stack.
empty())
683 auto const [node, otherNode] = stack.
top();
686 if (!node || !otherNode)
688 JLOG(journal_.info()) <<
"unable to fetch node";
691 else if (otherNode->getHash() != node->getHash())
693 JLOG(journal_.warn()) <<
"node hash mismatch";
699 if (!otherNode->isLeaf())
702 auto& otherNodePeek =
704 if (nodePeek->key() != otherNodePeek->key())
706 if (nodePeek->slice() != otherNodePeek->slice())
709 else if (node->isInner())
711 if (!otherNode->isInner())
715 for (
int i = 0; i < 16; ++i)
717 if (node_inner->isEmptyBranch(i))
719 if (!other_inner->isEmptyBranch(i))
724 if (other_inner->isEmptyBranch(i))
727 auto next = descend(node_inner, i);
728 auto otherNext = other.
descend(other_inner, i);
729 if (!next || !otherNext)
731 JLOG(journal_.warn()) <<
"unable to fetch inner node";
734 stack.
push({next, otherNext});
750 auto node = root_.get();
757 if (inner->isEmptyBranch(branch))
760 node = descendThrow(inner, branch);
764 return (node->isInner()) && (node->getHash() == targetNodeHash);
772 auto node = root_.get();
775 if (!node->isInner())
776 return node->getHash() == targetNodeHash;
782 if (inner->isEmptyBranch(branch))
785 if (inner->getChildHash(branch) ==
789 node = descendThrow(inner, branch);
791 }
while (node->isInner());
801 walkTowardsKey(key, &stack);
805 JLOG(journal_.debug()) <<
"no path to " << key;
809 if (
auto const& node = stack.
top().
first; !node || node->isInner() ||
810 std::static_pointer_cast<SHAMapLeafNode>(node)->peekItem()->key() !=
813 JLOG(journal_.debug()) <<
"no path to " << key;
818 path.reserve(stack.
size());
819 while (!stack.
empty())
822 stack.
top().
first->serializeForWire(s);
823 path.emplace_back(std::move(s.
modData()));
827 JLOG(journal_.debug()) <<
"getPath for key " << key <<
", path length "
833SHAMap::verifyProofPath(
838 if (path.empty() || path.size() > 65)
844 for (
auto rit = path.rbegin(); rit != path.rend(); ++rit)
846 auto const& blob = *rit;
847 auto node = SHAMapTreeNode::makeFromWire(
makeSlice(blob));
851 if (node->getHash() != hash)
857 auto nodeId = SHAMapNodeID::createID(depth, key);
864 return depth + 1 == path.size();
virtual std::shared_ptr< FullBelowCache > getFullBelowCache()=0
Return a pointer to the Family Full Below Cache.
bool isFullBelow(std::uint32_t generation) const
bool isEmptyBranch(int m) const
SHAMapHash const & getChildHash(int m) const
int getBranchCount() const
boost::intrusive_ptr< SHAMapItem const > const & peekItem() const
Identifies a node inside a SHAMap.
unsigned int getDepth() const
uint256 const & getNodeID() const
SHAMapNodeID getChildNodeID(unsigned int m) const
virtual void gotNode(bool fromFilter, SHAMapHash const &nodeHash, std::uint32_t ledgerSeq, Blob &&nodeData, SHAMapNodeType type) const =0
virtual bool isInner() const =0
Determines if this is an inner node.
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
void gmn_ProcessNodes(MissingNodes &, MissingNodes::StackEntry &node)
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
std::shared_ptr< SHAMapTreeNode > root_
bool hasInnerNode(SHAMapNodeID const &nodeID, SHAMapHash const &hash) const
Does this map have this inner node?
std::shared_ptr< SHAMapTreeNode > descendNoStore(std::shared_ptr< SHAMapInnerNode > const &, int branch) const
void visitNodes(std::function< bool(SHAMapTreeNode &)> const &function) const
Visit every node in this SHAMap.
bool hasLeafNode(uint256 const &tag, SHAMapHash const &hash) const
Does this map have this leaf node?
void visitDifferences(SHAMap const *have, std::function< bool(SHAMapTreeNode const &)> const &) const
Visit every node in this SHAMap that is not present in the specified SHAMap.
void visitLeaves(std::function< void(boost::intrusive_ptr< SHAMapItem const > const &)> const &) const
Visit every leaf node in this SHAMap.
An immutable linear range of bytes.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
unsigned int selectBranch(SHAMapNodeID const &id, uint256 const &hash)
Returns the branch that would contain the given hash.
@ pending
List will be valid in the future.
std::enable_if_t< std::is_integral< Integral >::value &&detail::is_engine< Engine >::value, Integral > rand_int(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
@ innerNode
inner node in V1 tree
std::set< SHAMapHash > missingHashes_
std::stack< StackEntry, std::deque< StackEntry > > stack_
std::condition_variable deferCondVar_
std::uint32_t generation_
std::vector< std::pair< SHAMapNodeID, uint256 > > missingNodes_
std::map< SHAMapInnerNode *, SHAMapNodeID > resumes_
SHAMapSyncFilter * filter_
std::vector< DeferredNode > finishedReads_