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>
19#include <boost/smart_ptr/intrusive_ptr.hpp>
36 std::function<
void(boost::intrusive_ptr<SHAMapItem const>
const& item)>
const& leafFunction)
54 if (!
root_->isInner())
67 if (!node->isEmptyBranch(pos))
70 if (!function(*child))
80 while ((pos !=
kBranchFactor - 1u) && (node->isEmptyBranch(pos + 1)))
86 stack.
emplace(pos + 1, std::move(node));
118 if (
root_->getHash().isZero())
121 if ((map !=
nullptr) && (
root_->getHash() == map->
root_->getHash()))
127 if ((map ==
nullptr) || !map->
hasLeafNode(leaf->peekItem()->key(), leaf->getHash()))
137 while (!stack.
empty())
139 auto const [node, nodeID] = stack.
top();
143 if (!function(*node))
155 UNREACHABLE(
"xrpl::SHAMap::visitDifferences : inner node at leaf depth");
163 if (!node->isEmptyBranch(i))
165 auto const& childHash = node->getChildHash(i);
166 auto const childID = nodeID.getChildNodeID(i);
171 if ((map ==
nullptr) || !map->
hasInnerNode(childID, childHash))
176 if (!function(*next))
193 auto& firstChild = std::get<2>(se);
194 auto& currentChild = std::get<3>(se);
195 bool& fullBelow = std::get<4>(se);
199 auto const branch = (firstChild + currentChild++) %
kBranchFactor;
210 else if (!
backed_ || !
f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
212 bool pending =
false;
220 std::unique_lock<std::mutex> const lock{mn.deferLock};
221 mn.
finishedReads.emplace_back(node, nodeID, branch, std::move(found));
230 else if (d ==
nullptr)
260 node->setFullBelowGen(mn.generation);
263 f_.getFullBelowCache()->insert(node->getHash().asUInt256());
288 auto parent = std::get<0>(deferredNode);
289 auto const& parentID = std::get<1>(deferredNode);
290 auto branch = std::get<2>(deferredNode);
291 auto nodePtr = std::get<3>(deferredNode);
292 auto const& nodeHash = parent->getChildHash(branch);
296 nodePtr = parent->canonicalizeChild(branch, std::move(nodePtr));
304 mn.
missingNodes.emplace_back(parentID.getChildNodeID(branch), nodeHash.asUInt256());
322 XRPL_ASSERT(
root_->getHash().isNonZero(),
"xrpl::SHAMap::getMissingNodes : nonzero root hash");
323 XRPL_ASSERT(max > 0,
"xrpl::SHAMap::getMissingNodes : valid max input");
329 f_.getFullBelowCache()->getGeneration());
331 if (!
root_->isInner() ||
346 auto& node = std::get<0>(pos);
347 auto& nextChild = std::get<3>(pos);
348 auto& fullBelow = std::get<4>(pos);
360 if ((node ==
nullptr) && !mn.
stack.empty())
363 bool const was = fullBelow;
365 pos = mn.
stack.top();
375 fullBelow = fullBelow && was;
377 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : first non-null node");
395 for (
auto const& [innerNode, nodeId] : mn.
resumes)
398 mn.
stack.emplace(innerNode, nodeId,
randInt(255), 0,
true);
404 if (!mn.
stack.empty())
407 pos = mn.
stack.top();
409 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : second non-null node");
417 }
while (node !=
nullptr);
435 auto node =
root_.get();
438 while ((node !=
nullptr) && node->isInner() && (nodeID.
getDepth() < wanted.
getDepth()))
442 if (inner->isEmptyBranch(branch))
448 if (node ==
nullptr || wanted != nodeID)
450 JLOG(
journal_.info()) <<
"peer requested node that is not in the map: " << wanted
451 <<
" but found " << nodeID;
457 JLOG(
journal_.warn()) <<
"peer requests empty node";
462 stack.
emplace(node, nodeID, depth);
466 while (!stack.
empty())
473 node->serializeForWire(s);
474 data.emplace_back(nodeID, node->isLeaf(), s.
getData());
481 auto const bc = inner->getBranchCount();
483 if ((depth > 0) || (bc == 1))
488 if (!inner->isEmptyBranch(i))
493 if (childNode->isInner() && ((depth > 1) || (bc == 1)))
497 stack.
emplace(childNode, childID, (bc > 1) ? (depth - 1) : depth);
499 else if (childNode->isInner() || fatLeaves)
503 childNode->serializeForWire(s);
504 data.emplace_back(childID, childNode->isLeaf(), s.
getData());
518 root_->serializeForWire(s);
527 XRPL_ASSERT(
cowid_ >= 1,
"xrpl::SHAMap::addRootNode : valid cowid");
528 XRPL_ASSERT(rootNode,
"xrpl::SHAMap::addRootNode : non-null root node");
531 if (
root_->getHash().isNonZero())
533 JLOG(
journal_.trace()) <<
"Got root node, already have one";
534 XRPL_ASSERT(
root_->getHash() == hash,
"xrpl::SHAMap::addRootNode : valid hash");
538 if (rootNode->getHash() != hash)
540 JLOG(
journal_.warn()) <<
"Corrupt root node received: expected hash " << hash <<
", got "
541 << rootNode->getHash();
548 root_ = std::move(rootNode);
553 if (filter !=
nullptr)
556 root_->serializeWithPrefix(s);
570 XRPL_ASSERT(!nodeID.
isRoot(),
"xrpl::SHAMap::addKnownNode : valid node");
571 XRPL_ASSERT(treeNode,
"xrpl::SHAMap::addKnownNode : non-null tree node");
575 "xrpl::SHAMap::addKnownNode : leaf position consistent with node ID");
579 JLOG(
journal_.trace()) <<
"AddKnownNode while not synching";
583 auto const generation =
f_.getFullBelowCache()->getGeneration();
585 auto currNode =
root_.get();
587 while (currNode->isInner() &&
593 if (inner->isEmptyBranch(branch))
595 JLOG(
journal_.warn()) <<
"Add known node " << nodeID <<
" for empty branch " << branch
596 <<
" at " << currNodeID;
600 auto childHash = inner->getChildHash(branch);
601 if (
f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
606 auto prevNode = inner;
607 std::tie(currNode, currNodeID) =
descend(inner, currNodeID, branch, filter);
609 if (currNode !=
nullptr)
612 if (childHash != treeNode->getHash())
614 JLOG(
journal_.warn()) <<
"Corrupt node " << nodeID <<
" received: expected hash "
615 << childHash <<
", got " << treeNode->getHash();
630 if (currNodeID != nodeID)
633 JLOG(
journal_.warn()) <<
"unable to hook node " << nodeID;
634 JLOG(
journal_.info()) <<
" stuck at " << currNodeID;
636 <<
", walked to= " << currNodeID.
getDepth();
643 treeNode = prevNode->canonicalizeChild(branch, std::move(treeNode));
645 if (filter !=
nullptr)
648 treeNode->serializeWithPrefix(s);
656 JLOG(
journal_.trace()) <<
"got node, already had it (late)";
668 while (!stack.
empty())
670 auto const [node, otherNode] = stack.
top();
673 if ((node ==
nullptr) || (otherNode ==
nullptr))
675 JLOG(
journal_.info()) <<
"unable to fetch node";
678 if (otherNode->getHash() != node->getHash())
680 JLOG(
journal_.warn()) <<
"node hash mismatch";
686 if (!otherNode->isLeaf())
690 if (nodePeek->key() != otherNodePeek->key())
692 if (nodePeek->slice() != otherNodePeek->slice())
695 else if (node->isInner())
697 if (!otherNode->isInner())
703 if (nodeInner->isEmptyBranch(i))
705 if (!otherInner->isEmptyBranch(i))
710 if (otherInner->isEmptyBranch(i))
713 auto next =
descend(nodeInner, i);
714 auto otherNext = other.
descend(otherInner, i);
715 if ((next ==
nullptr) || (otherNext ==
nullptr))
717 JLOG(
journal_.warn()) <<
"unable to fetch inner node";
720 stack.
emplace(next, otherNext);
735 auto node =
root_.get();
742 if (inner->isEmptyBranch(branch))
749 return (node->isInner()) && (node->getHash() == targetNodeHash);
758 auto node =
root_.get();
761 if (!node->isInner())
762 return node->getHash() == targetNodeHash;
772 UNREACHABLE(
"xrpl::SHAMap::hasLeafNode : inner node at leaf depth");
779 if (inner->isEmptyBranch(branch))
782 if (inner->getChildHash(branch) == targetNodeHash)
787 }
while (node->isInner());
801 JLOG(
journal_.debug()) <<
"no path to " << key;
805 if (
auto const& node = stack.
top().first; !node || node->isInner() ||
808 JLOG(
journal_.debug()) <<
"no path to " << key;
814 while (!stack.
empty())
817 stack.
top().first->serializeForWire(s);
822 JLOG(
journal_.debug()) <<
"getPath for key " << key <<
", path length " <<
path.size();
835 for (
auto rit =
path.rbegin(); rit !=
path.rend(); ++rit)
837 auto const& blob = *rit;
842 if (node->getHash() != hash)
852 depth >=
kLeafDepth,
"xrpl::SHAMap::verifyProofPath : inner at leaf depth");
870 return depth + 1 ==
path.size();
static SHAMapAddNode duplicate()
static SHAMapAddNode useful()
static SHAMapAddNode invalid()
SHAMapHash const & getChildHash(unsigned int branch) const
bool isEmptyBranch(unsigned int branch) const
Identifies a node inside a SHAMap.
bool isPrefixOf(UInt256 const &key) const
Test whether this node ID lies on the path to the given leaf key.
SHAMapNodeID getChildNodeID(unsigned int branch) const
UInt256 const & getNodeID() const
static SHAMapNodeID createID(unsigned int depth, UInt256 const &key)
Create a SHAMapNodeID of a node with the depth of the node and the key of a leaf.
unsigned int getDepth() 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.
A path from the root of the map down to some node, pairing each node with the ID naming its position.
std::pair< SHAMapTreeNodePtr, SHAMapNodeID > const & top() const
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, unsigned int branch, SHAMapSyncFilter const *filter, bool &pending, DescendCallback &&) const
bool hasLeafNode(UInt256 const &tag, SHAMapHash const &hash) const
Does this map have this leaf node?
SHAMapTreeNode * descend(SHAMapInnerNode *, unsigned int branch) const
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
static constexpr unsigned int kLeafDepth
The depth of the hash map: data is only present in the leaves.
bool getNodeFat(SHAMapNodeID const &wanted, std::vector< SHAMapNodeData > &data, bool fatLeaves, std::uint32_t depth) const
static void gmnProcessDeferredReads(MissingNodes &)
static constexpr unsigned int kBranchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map).
SHAMapTreeNodePtr descendNoStore(SHAMapInnerNode &, unsigned int branch) 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?
bool deepCompare(SHAMap &other) const
void gmnProcessNodes(MissingNodes &, MissingNodes::StackEntry &node)
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, unsigned int branch) const
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.
void visitLeaves(std::function< void(boost::intrusive_ptr< SHAMapItem const > const &)> const &) const
Visit every leaf node in this SHAMap.
SHAMapLeafNode * walkTowardsKey(UInt256 const &id, NodePathStack *stack=nullptr) const
Walk towards the specified id, returning the node.
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
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
std::vector< std::pair< SHAMapNodeID, UInt256 > > getMissingNodes(int maxNodes, SHAMapSyncFilter const *filter)
Check for nodes in the SHAMap not available.
void visitNodes(std::function< bool(SHAMapTreeNode &)> const &function) const
Visit every node in this SHAMap.
std::optional< std::vector< Blob > > getProofPath(UInt256 const &key) const
Get the proof path of the key.
boost::intrusive_ptr< SHAMapItem const > const & peekItem(UInt256 const &id) const
static bool verifyProofPath(UInt256 const &rootHash, UInt256 const &key, std::vector< Blob > const &path)
Verify the proof path.
T * get() const
Get the raw pointer.
SharedPtr< T > staticPointerCast(TT const &v)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
intr_ptr::SharedPtr< SHAMapTreeNode > SHAMapTreeNodePtr
Slice makeSlice(std::array< T, N > const &a)
UInt256 const & leafKey(SHAMapTreeNode const &node)
Return the key of the item held by a SHAMap leaf node.
Integral randInt(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
Dest safeDowncast(Src *s) noexcept
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.
std::tuple< SHAMapInnerNode *, SHAMapNodeID, unsigned int, unsigned int, bool > StackEntry
std::vector< std::pair< SHAMapNodeID, UInt256 > > missingNodes
std::set< SHAMapHash > missingHashes
std::tuple< SHAMapInnerNode *, SHAMapNodeID, unsigned int, SHAMapTreeNodePtr > DeferredNode
std::vector< DeferredNode > finishedReads
SHAMapSyncFilter const * filter
std::map< SHAMapInnerNode *, SHAMapNodeID > resumes
std::stack< StackEntry, std::deque< StackEntry > > stack
std::condition_variable deferCondVar