1#include <xrpl/basics/random.h>
2#include <xrpl/basics/safe_cast.h>
3#include <xrpl/shamap/SHAMap.h>
4#include <xrpl/shamap/SHAMapLeafNode.h>
5#include <xrpl/shamap/SHAMapSyncFilter.h>
11 std::function<
void(boost::intrusive_ptr<SHAMapItem const>
const& item)>
const& leafFunction)
16 leafFunction(safe_downcast<SHAMapLeafNode&>(node).
peekItem());
29 if (!
root_->isInner())
35 auto node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
root_);
42 if (!node->isEmptyBranch(pos))
45 if (!function(*child))
55 while ((pos != 15) && (node->isEmptyBranch(pos + 1)))
65 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(child);
93 if (
root_->getHash().isZero())
96 if ((have !=
nullptr) && (
root_->getHash() == have->
root_->getHash()))
101 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(
root_);
102 if ((have ==
nullptr) || !have->
hasLeafNode(leaf->peekItem()->key(), leaf->getHash()))
112 while (!stack.
empty())
114 auto const [node, nodeID] = stack.
top();
118 if (!function(*node))
122 for (
int i = 0; i < 16; ++i)
124 if (!node->isEmptyBranch(i))
126 auto const& childHash = node->getChildHash(i);
132 if ((have ==
nullptr) || !have->
hasInnerNode(childID, childHash))
133 stack.
push({safe_downcast<SHAMapInnerNode*>(next), childID});
138 safe_downcast<SHAMapLeafNode*>(next)->
peekItem()->key(), childHash))
140 if (!function(*next))
161 while (currentChild < 16)
163 int const branch = (firstChild + currentChild++) % 16;
182 [node, nodeID, branch, &mn](
185 std::unique_lock<std::mutex> const lock{mn.deferLock_};
186 mn.
finishedReads_.emplace_back(node, nodeID, branch, std::move(found));
195 else if (d ==
nullptr)
208 d->isInner() && !safe_downcast<SHAMapInnerNode*>(d)->isFullBelow(mn.
generation_))
213 node = safe_downcast<SHAMapInnerNode*>(d);
227 node->setFullBelowGen(mn.generation_);
230 f_.getFullBelowCache()->insert(node->getHash().as_uint256());
260 auto const& nodeHash = parent->getChildHash(branch);
264 nodePtr = parent->canonicalizeChild(branch, std::move(nodePtr));
272 mn.
missingNodes_.emplace_back(parentID.getChildNodeID(branch), nodeHash.as_uint256());
289 XRPL_ASSERT(root_->getHash().isNonZero(),
"xrpl::SHAMap::getMissingNodes : nonzero root hash");
290 XRPL_ASSERT(max > 0,
"xrpl::SHAMap::getMissingNodes : valid max input");
296 f_.getFullBelowCache()->getGeneration());
298 if (!root_->isInner() ||
299 intr_ptr::static_pointer_cast<SHAMapInnerNode>(root_)->isFullBelow(mn.
generation_))
322 gmn_ProcessNodes(mn, pos);
327 if ((node ==
nullptr) && !mn.
stack_.empty())
330 bool const was = fullBelow;
342 fullBelow = fullBelow && was;
344 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : first non-null node");
351 gmn_ProcessDeferredReads(mn);
376 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : second non-null node");
384 }
while (node !=
nullptr);
402 auto node = root_.get();
405 while ((node !=
nullptr) && node->isInner() && (nodeID.
getDepth() < wanted.
getDepth()))
408 auto inner = safe_downcast<SHAMapInnerNode*>(node);
409 if (inner->isEmptyBranch(branch))
411 node = descendThrow(inner, branch);
415 if (node ==
nullptr || wanted != nodeID)
417 JLOG(journal_.info()) <<
"peer requested node that is not in the map: " << wanted
418 <<
" but found " << nodeID;
422 if (node->isInner() && safe_downcast<SHAMapInnerNode*>(node)->isEmpty())
424 JLOG(journal_.warn()) <<
"peer requests empty node";
429 stack.
emplace(node, nodeID, depth);
433 while (!stack.
empty())
440 node->serializeForWire(s);
447 auto inner = safe_downcast<SHAMapInnerNode*>(node);
448 int const bc = inner->getBranchCount();
450 if ((depth > 0) || (bc == 1))
453 for (
int i = 0; i < 16; ++i)
455 if (!inner->isEmptyBranch(i))
457 auto const childNode = descendThrow(inner, i);
460 if (childNode->isInner() && ((depth > 1) || (bc == 1)))
464 stack.
emplace(childNode, childID, (bc > 1) ? (depth - 1) : depth);
466 else if (childNode->isInner() || fatLeaves)
470 childNode->serializeForWire(s);
485 root_->serializeForWire(s);
492 if (root_->getHash().isNonZero())
494 JLOG(journal_.trace()) <<
"got root node, already have one";
495 XRPL_ASSERT(root_->getHash() == hash,
"xrpl::SHAMap::addRootNode : valid hash input");
496 return SHAMapAddNode::duplicate();
499 XRPL_ASSERT(cowid_ >= 1,
"xrpl::SHAMap::addRootNode : valid cowid");
500 auto node = SHAMapTreeNode::makeFromWire(rootNode);
501 if (!node || node->getHash() != hash)
502 return SHAMapAddNode::invalid();
505 canonicalize(hash, node);
512 if (filter !=
nullptr)
515 root_->serializeWithPrefix(s);
517 false, root_->getHash(), ledgerSeq_, std::move(s.
modData()), root_->getType());
520 return SHAMapAddNode::useful();
526 XRPL_ASSERT(!node.
isRoot(),
"xrpl::SHAMap::addKnownNode : valid node input");
530 JLOG(journal_.trace()) <<
"AddKnownNode while not synching";
531 return SHAMapAddNode::duplicate();
534 auto const generation = f_.getFullBelowCache()->getGeneration();
536 auto currNode = root_.get();
538 while (currNode->isInner() &&
539 !safe_downcast<SHAMapInnerNode*>(currNode)->isFullBelow(generation) &&
543 XRPL_ASSERT(branch >= 0,
"xrpl::SHAMap::addKnownNode : valid branch");
544 auto inner = safe_downcast<SHAMapInnerNode*>(currNode);
545 if (inner->isEmptyBranch(branch))
547 JLOG(journal_.warn()) <<
"Add known node for empty branch" << node;
548 return SHAMapAddNode::invalid();
551 auto childHash = inner->getChildHash(branch);
552 if (f_.getFullBelowCache()->touch_if_exists(childHash.as_uint256()))
554 return SHAMapAddNode::duplicate();
557 auto prevNode = inner;
558 std::tie(currNode, currNodeID) = descend(inner, currNodeID, branch, filter);
560 if (currNode !=
nullptr)
563 auto newNode = SHAMapTreeNode::makeFromWire(rawNode);
565 if (!newNode || childHash != newNode->getHash())
567 JLOG(journal_.warn()) <<
"Corrupt node received";
568 return SHAMapAddNode::invalid();
576 if (newNode->isLeaf())
578 auto const& actualKey =
579 safe_downcast<SHAMapLeafNode const*>(newNode.get())->peekItem()->key();
582 auto const expectedNodeID = SHAMapNodeID::createID(node.
getDepth(), actualKey);
583 if (expectedNodeID.getNodeID() != node.
getNodeID())
585 JLOG(journal_.debug())
586 <<
"Leaf node position mismatch: "
587 <<
"expected=" << expectedNodeID.getNodeID() <<
", actual=" << node.
getNodeID();
588 return SHAMapAddNode::invalid();
595 if ((currNodeID.
getDepth() > leafDepth) ||
596 (newNode->isInner() && currNodeID.
getDepth() == leafDepth))
599 state_ = SHAMapState::Invalid;
600 return SHAMapAddNode::useful();
603 if (currNodeID != node)
606 JLOG(journal_.warn()) <<
"unable to hook node " << node;
607 JLOG(journal_.info()) <<
" stuck at " << currNodeID;
608 JLOG(journal_.info()) <<
"got depth=" << node.
getDepth()
609 <<
", walked to= " << currNodeID.
getDepth();
610 return SHAMapAddNode::useful();
614 canonicalize(childHash, newNode);
616 newNode = prevNode->canonicalizeChild(branch, std::move(newNode));
618 if (filter !=
nullptr)
621 newNode->serializeWithPrefix(s);
623 false, childHash, ledgerSeq_, std::move(s.
modData()), newNode->getType());
626 return SHAMapAddNode::useful();
629 JLOG(journal_.trace()) <<
"got node, already had it (late)";
630 return SHAMapAddNode::duplicate();
639 stack.
push({root_.get(), other.
root_.get()});
641 while (!stack.
empty())
643 auto const [node, otherNode] = stack.
top();
646 if ((node ==
nullptr) || (otherNode ==
nullptr))
648 JLOG(journal_.info()) <<
"unable to fetch node";
651 if (otherNode->getHash() != node->getHash())
653 JLOG(journal_.warn()) <<
"node hash mismatch";
659 if (!otherNode->isLeaf())
661 auto& nodePeek = safe_downcast<SHAMapLeafNode*>(node)->peekItem();
662 auto& otherNodePeek = safe_downcast<SHAMapLeafNode*>(otherNode)->peekItem();
663 if (nodePeek->key() != otherNodePeek->key())
665 if (nodePeek->slice() != otherNodePeek->slice())
668 else if (node->isInner())
670 if (!otherNode->isInner())
672 auto node_inner = safe_downcast<SHAMapInnerNode*>(node);
673 auto other_inner = safe_downcast<SHAMapInnerNode*>(otherNode);
674 for (
int i = 0; i < 16; ++i)
676 if (node_inner->isEmptyBranch(i))
678 if (!other_inner->isEmptyBranch(i))
683 if (other_inner->isEmptyBranch(i))
686 auto next = descend(node_inner, i);
687 auto otherNext = other.
descend(other_inner, i);
688 if ((next ==
nullptr) || (otherNext ==
nullptr))
690 JLOG(journal_.warn()) <<
"unable to fetch inner node";
693 stack.
push({next, otherNext});
707 auto node = root_.get();
713 auto inner = safe_downcast<SHAMapInnerNode*>(node);
714 if (inner->isEmptyBranch(branch))
717 node = descendThrow(inner, branch);
721 return (node->isInner()) && (node->getHash() == targetNodeHash);
729 auto node = root_.get();
732 if (!node->isInner())
733 return node->getHash() == targetNodeHash;
738 auto inner = safe_downcast<SHAMapInnerNode*>(node);
739 if (inner->isEmptyBranch(branch))
742 if (inner->getChildHash(branch) == targetNodeHash)
745 node = descendThrow(inner, branch);
747 }
while (node->isInner());
757 walkTowardsKey(key, &stack);
761 JLOG(journal_.debug()) <<
"no path to " << key;
765 if (
auto const& node = stack.
top().
first; !node || node->isInner() ||
766 intr_ptr::static_pointer_cast<SHAMapLeafNode>(node)->peekItem()->key() != key)
768 JLOG(journal_.debug()) <<
"no path to " << key;
774 while (!stack.
empty())
777 stack.
top().
first->serializeForWire(s);
782 JLOG(journal_.debug()) <<
"getPath for key " << key <<
", path length " << path.
size();
795 for (
auto rit = path.
rbegin(); rit != path.
rend(); ++rit)
797 auto const& blob = *rit;
798 auto node = SHAMapTreeNode::makeFromWire(
makeSlice(blob));
802 if (node->getHash() != hash)
808 auto nodeId = SHAMapNodeID::createID(depth, key);
809 hash = safe_downcast<SHAMapInnerNode*>(node.get())
815 return depth + 1 == path.
size();
virtual std::shared_ptr< FullBelowCache > getFullBelowCache()=0
Return a pointer to the Family Full Below Cache.
SHAMapHash const & getChildHash(int m) const
bool isEmptyBranch(int m) const
Identifies a node inside a SHAMap.
uint256 const & getNodeID() const
SHAMapNodeID getChildNodeID(unsigned int m) const
unsigned int getDepth() 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 * descend(SHAMapInnerNode *, int branch) const
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
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)
void visitLeaves(std::function< void(boost::intrusive_ptr< SHAMapItem const > const &)> const &) const
Visit every leaf node in this SHAMap.
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
void visitNodes(std::function< bool(SHAMapTreeNode &)> const &function) const
Visit every node in this SHAMap.
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
intr_ptr::SharedPtr< SHAMapTreeNode > root_
A shared intrusive pointer class that supports weak pointers.
An immutable linear range of bytes.
T emplace_back(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
@ 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)
std::condition_variable deferCondVar_
std::map< SHAMapInnerNode *, SHAMapNodeID > resumes_
std::vector< DeferredNode > finishedReads_
std::set< SHAMapHash > missingHashes_
std::vector< std::pair< SHAMapNodeID, uint256 > > missingNodes_
std::stack< StackEntry, std::deque< StackEntry > > stack_
SHAMapSyncFilter * filter_
std::uint32_t generation_