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 != 15) && (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))
147 for (
int i = 0; i < 16; ++i)
149 if (!node->isEmptyBranch(i))
151 auto const& childHash = node->getChildHash(i);
152 auto const childID = nodeID.getChildNodeID(i);
157 if ((map ==
nullptr) || !map->
hasInnerNode(childID, childHash))
162 if (!function(*next))
179 int& firstChild = std::get<2>(se);
180 int& currentChild = std::get<3>(se);
181 bool& fullBelow = std::get<4>(se);
183 while (currentChild < 16)
185 int const branch = (firstChild + currentChild++) % 16;
196 else if (!
backed_ || !
f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
198 bool pending =
false;
206 std::unique_lock<std::mutex> const lock{mn.deferLock};
207 mn.
finishedReads.emplace_back(node, nodeID, branch, std::move(found));
216 else if (d ==
nullptr)
246 node->setFullBelowGen(mn.generation);
249 f_.getFullBelowCache()->insert(node->getHash().asUInt256());
274 auto parent = std::get<0>(deferredNode);
275 auto const& parentID = std::get<1>(deferredNode);
276 auto branch = std::get<2>(deferredNode);
277 auto nodePtr = std::get<3>(deferredNode);
278 auto const& nodeHash = parent->getChildHash(branch);
282 nodePtr = parent->canonicalizeChild(branch, std::move(nodePtr));
290 mn.
missingNodes.emplace_back(parentID.getChildNodeID(branch), nodeHash.asUInt256());
308 XRPL_ASSERT(
root_->getHash().isNonZero(),
"xrpl::SHAMap::getMissingNodes : nonzero root hash");
309 XRPL_ASSERT(max > 0,
"xrpl::SHAMap::getMissingNodes : valid max input");
315 f_.getFullBelowCache()->getGeneration());
317 if (!
root_->isInner() ||
332 auto& node = std::get<0>(pos);
333 auto& nextChild = std::get<3>(pos);
334 auto& fullBelow = std::get<4>(pos);
346 if ((node ==
nullptr) && !mn.
stack.empty())
349 bool const was = fullBelow;
351 pos = mn.
stack.top();
361 fullBelow = fullBelow && was;
363 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : first non-null node");
381 for (
auto const& [innerNode, nodeId] : mn.
resumes)
384 mn.
stack.emplace(innerNode, nodeId,
randInt(255), 0,
true);
390 if (!mn.
stack.empty())
393 pos = mn.
stack.top();
395 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : second non-null node");
403 }
while (node !=
nullptr);
421 auto node =
root_.get();
424 while ((node !=
nullptr) && node->isInner() && (nodeID.
getDepth() < wanted.
getDepth()))
428 if (inner->isEmptyBranch(branch))
434 if (node ==
nullptr || wanted != nodeID)
436 JLOG(
journal_.info()) <<
"peer requested node that is not in the map: " << wanted
437 <<
" but found " << nodeID;
443 JLOG(
journal_.warn()) <<
"peer requests empty node";
448 stack.
emplace(node, nodeID, depth);
452 while (!stack.
empty())
459 node->serializeForWire(s);
460 data.emplace_back(nodeID, node->isLeaf(), s.
getData());
467 int const bc = inner->getBranchCount();
469 if ((depth > 0) || (bc == 1))
472 for (
int i = 0; i < 16; ++i)
474 if (!inner->isEmptyBranch(i))
479 if (childNode->isInner() && ((depth > 1) || (bc == 1)))
483 stack.
emplace(childNode, childID, (bc > 1) ? (depth - 1) : depth);
485 else if (childNode->isInner() || fatLeaves)
489 childNode->serializeForWire(s);
490 data.emplace_back(childID, childNode->isLeaf(), s.
getData());
504 root_->serializeForWire(s);
513 XRPL_ASSERT(
cowid_ >= 1,
"xrpl::SHAMap::addRootNode : valid cowid");
514 XRPL_ASSERT(rootNode,
"xrpl::SHAMap::addRootNode : non-null root node");
517 if (
root_->getHash().isNonZero())
519 JLOG(
journal_.trace()) <<
"Got root node, already have one";
520 XRPL_ASSERT(
root_->getHash() == hash,
"xrpl::SHAMap::addRootNode : valid hash");
524 if (rootNode->getHash() != hash)
526 JLOG(
journal_.warn()) <<
"Corrupt root node received: expected hash " << hash <<
", got "
527 << rootNode->getHash();
534 root_ = std::move(rootNode);
539 if (filter !=
nullptr)
542 root_->serializeWithPrefix(s);
556 XRPL_ASSERT(!nodeID.
isRoot(),
"xrpl::SHAMap::addKnownNode : valid node");
557 XRPL_ASSERT(treeNode,
"xrpl::SHAMap::addKnownNode : non-null tree node");
559 !treeNode->isLeaf() ||
562 "xrpl::SHAMap::addKnownNode : leaf position consistent with node ID");
566 JLOG(
journal_.trace()) <<
"AddKnownNode while not synching";
570 auto const generation =
f_.getFullBelowCache()->getGeneration();
572 auto currNode =
root_.get();
574 while (currNode->isInner() &&
579 XRPL_ASSERT(branch >= 0,
"xrpl::SHAMap::addKnownNode : valid branch");
581 if (inner->isEmptyBranch(branch))
583 JLOG(
journal_.warn()) <<
"Add known node " << nodeID <<
" for empty branch " << branch
584 <<
" at " << currNodeID;
588 auto childHash = inner->getChildHash(branch);
589 if (
f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
594 auto prevNode = inner;
595 std::tie(currNode, currNodeID) =
descend(inner, currNodeID, branch, filter);
597 if (currNode !=
nullptr)
600 if (childHash != treeNode->getHash())
602 JLOG(
journal_.warn()) <<
"Corrupt node " << nodeID <<
" received: expected hash "
603 << childHash <<
", got " << treeNode->getHash();
618 if (currNodeID != nodeID)
621 JLOG(
journal_.warn()) <<
"unable to hook node " << nodeID;
622 JLOG(
journal_.info()) <<
" stuck at " << currNodeID;
624 <<
", walked to= " << currNodeID.
getDepth();
631 treeNode = prevNode->canonicalizeChild(branch, std::move(treeNode));
633 if (filter !=
nullptr)
636 treeNode->serializeWithPrefix(s);
644 JLOG(
journal_.trace()) <<
"got node, already had it (late)";
656 while (!stack.
empty())
658 auto const [node, otherNode] = stack.
top();
661 if ((node ==
nullptr) || (otherNode ==
nullptr))
663 JLOG(
journal_.info()) <<
"unable to fetch node";
666 if (otherNode->getHash() != node->getHash())
668 JLOG(
journal_.warn()) <<
"node hash mismatch";
674 if (!otherNode->isLeaf())
678 if (nodePeek->key() != otherNodePeek->key())
680 if (nodePeek->slice() != otherNodePeek->slice())
683 else if (node->isInner())
685 if (!otherNode->isInner())
689 for (
int i = 0; i < 16; ++i)
691 if (nodeInner->isEmptyBranch(i))
693 if (!otherInner->isEmptyBranch(i))
698 if (otherInner->isEmptyBranch(i))
701 auto next =
descend(nodeInner, i);
702 auto otherNext = other.
descend(otherInner, i);
703 if ((next ==
nullptr) || (otherNext ==
nullptr))
705 JLOG(
journal_.warn()) <<
"unable to fetch inner node";
708 stack.
emplace(next, otherNext);
723 auto node =
root_.get();
730 if (inner->isEmptyBranch(branch))
737 return (node->isInner()) && (node->getHash() == targetNodeHash);
746 auto node =
root_.get();
749 if (!node->isInner())
750 return node->getHash() == targetNodeHash;
756 if (inner->isEmptyBranch(branch))
759 if (inner->getChildHash(branch) == targetNodeHash)
764 }
while (node->isInner());
778 JLOG(
journal_.debug()) <<
"no path to " << key;
782 if (
auto const& node = stack.
top().first; !node || node->isInner() ||
785 JLOG(
journal_.debug()) <<
"no path to " << key;
791 while (!stack.
empty())
794 stack.
top().first->serializeForWire(s);
799 JLOG(
journal_.debug()) <<
"getPath for key " << key <<
", path length " <<
path.size();
806 if (
path.empty() ||
path.size() > 65)
812 for (
auto rit =
path.rbegin(); rit !=
path.rend(); ++rit)
814 auto const& blob = *rit;
819 if (node->getHash() != hash)
832 return depth + 1 ==
path.size();
static SHAMapAddNode duplicate()
static SHAMapAddNode useful()
static SHAMapAddNode invalid()
SHAMapHash const & getChildHash(int m) const
bool isEmptyBranch(int m) const
Identifies a node inside a SHAMap.
uint256 const & getNodeID() const
static SHAMapNodeID createID(int depth, uint256 const &key)
Create a SHAMapNodeID of a node with the depth of the node and the key of a leaf.
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
static SHAMapTreeNodePtr makeFromWire(Slice rawNode)
virtual bool isInner() const =0
Determines if this is an inner node.
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
static bool verifyProofPath(uint256 const &rootHash, uint256 const &key, std::vector< Blob > const &path)
Verify the proof path.
std::optional< std::vector< Blob > > getProofPath(uint256 const &key) const
Get the proof path of the key.
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
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
std::vector< std::pair< SHAMapNodeID, uint256 > > getMissingNodes(int maxNodes, SHAMapSyncFilter const *filter)
Check for nodes in the SHAMap not available.
static void gmnProcessDeferredReads(MissingNodes &)
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)
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.
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter const *filter, bool &pending, descendCallback &&) const
std::stack< std::pair< SHAMapTreeNodePtr, SHAMapNodeID > > SharedPtrNodeStack
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
bool hasLeafNode(uint256 const &tag, SHAMapHash const &hash) const
Does this map have this leaf node?
SHAMapTreeNodePtr descendNoStore(SHAMapInnerNode &, int branch) const
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
void visitNodes(std::function< bool(SHAMapTreeNode &)> const &function) const
Visit every node in this SHAMap.
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
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
Dest safeDowncast(Src *s) noexcept
Integral randInt(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
uint256 const & leafKey(SHAMapTreeNode const &node)
Return the key of the item held by a SHAMap leaf node.
Slice makeSlice(std::array< T, N > const &a)
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::set< SHAMapHash > missingHashes
std::vector< DeferredNode > finishedReads
std::vector< std::pair< SHAMapNodeID, uint256 > > missingNodes
SHAMapSyncFilter const * filter
std::map< SHAMapInnerNode *, SHAMapNodeID > resumes
std::tuple< SHAMapInnerNode *, SHAMapNodeID, int, int, bool > StackEntry
std::stack< StackEntry, std::deque< StackEntry > > stack
std::condition_variable deferCondVar