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 ((have !=
nullptr) && (
root_->getHash() == have->
root_->getHash()))
127 if ((have ==
nullptr) || !have->
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);
157 if ((have ==
nullptr) || !have->
hasInnerNode(childID, childHash))
165 if (!function(*next))
182 int& firstChild = std::get<2>(se);
183 int& currentChild = std::get<3>(se);
184 bool& fullBelow = std::get<4>(se);
186 while (currentChild < 16)
188 int const branch = (firstChild + currentChild++) % 16;
199 else if (!
backed_ || !
f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
201 bool pending =
false;
209 std::unique_lock<std::mutex> const lock{mn.deferLock};
210 mn.
finishedReads.emplace_back(node, nodeID, branch, std::move(found));
219 else if (d ==
nullptr)
249 node->setFullBelowGen(mn.generation);
252 f_.getFullBelowCache()->insert(node->getHash().asUInt256());
277 auto parent = std::get<0>(deferredNode);
278 auto const& parentID = std::get<1>(deferredNode);
279 auto branch = std::get<2>(deferredNode);
280 auto nodePtr = std::get<3>(deferredNode);
281 auto const& nodeHash = parent->getChildHash(branch);
285 nodePtr = parent->canonicalizeChild(branch, std::move(nodePtr));
293 mn.
missingNodes.emplace_back(parentID.getChildNodeID(branch), nodeHash.asUInt256());
310 XRPL_ASSERT(
root_->getHash().isNonZero(),
"xrpl::SHAMap::getMissingNodes : nonzero root hash");
311 XRPL_ASSERT(max > 0,
"xrpl::SHAMap::getMissingNodes : valid max input");
317 f_.getFullBelowCache()->getGeneration());
319 if (!
root_->isInner() ||
334 auto& node = std::get<0>(pos);
335 auto& nextChild = std::get<3>(pos);
336 auto& fullBelow = std::get<4>(pos);
348 if ((node ==
nullptr) && !mn.
stack.empty())
351 bool const was = fullBelow;
353 pos = mn.
stack.top();
363 fullBelow = fullBelow && was;
365 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : first non-null node");
383 for (
auto const& [innerNode, nodeId] : mn.
resumes)
386 mn.
stack.emplace(innerNode, nodeId,
randInt(255), 0,
true);
392 if (!mn.
stack.empty())
395 pos = mn.
stack.top();
397 XRPL_ASSERT(node,
"xrpl::SHAMap::getMissingNodes : second non-null node");
405 }
while (node !=
nullptr);
423 auto node =
root_.get();
426 while ((node !=
nullptr) && node->isInner() && (nodeID.
getDepth() < wanted.
getDepth()))
430 if (inner->isEmptyBranch(branch))
436 if (node ==
nullptr || wanted != nodeID)
438 JLOG(
journal_.info()) <<
"peer requested node that is not in the map: " << wanted
439 <<
" but found " << nodeID;
445 JLOG(
journal_.warn()) <<
"peer requests empty node";
450 stack.
emplace(node, nodeID, depth);
454 while (!stack.
empty())
461 node->serializeForWire(s);
462 data.emplace_back(nodeID, s.
getData());
469 int const bc = inner->getBranchCount();
471 if ((depth > 0) || (bc == 1))
474 for (
int i = 0; i < 16; ++i)
476 if (!inner->isEmptyBranch(i))
481 if (childNode->isInner() && ((depth > 1) || (bc == 1)))
485 stack.
emplace(childNode, childID, (bc > 1) ? (depth - 1) : depth);
487 else if (childNode->isInner() || fatLeaves)
491 childNode->serializeForWire(s);
492 data.emplace_back(childID, s.
getData());
506 root_->serializeForWire(s);
513 if (
root_->getHash().isNonZero())
515 JLOG(
journal_.trace()) <<
"got root node, already have one";
516 XRPL_ASSERT(
root_->getHash() == hash,
"xrpl::SHAMap::addRootNode : valid hash input");
520 XRPL_ASSERT(
cowid_ >= 1,
"xrpl::SHAMap::addRootNode : valid cowid");
522 if (!node || node->getHash() != hash)
533 if (filter !=
nullptr)
536 root_->serializeWithPrefix(s);
547 XRPL_ASSERT(!node.
isRoot(),
"xrpl::SHAMap::addKnownNode : valid node input");
551 JLOG(
journal_.trace()) <<
"AddKnownNode while not synching";
555 auto const generation =
f_.getFullBelowCache()->getGeneration();
557 auto currNode =
root_.get();
559 while (currNode->isInner() &&
564 XRPL_ASSERT(branch >= 0,
"xrpl::SHAMap::addKnownNode : valid branch");
566 if (inner->isEmptyBranch(branch))
568 JLOG(
journal_.warn()) <<
"Add known node for empty branch" << node;
572 auto childHash = inner->getChildHash(branch);
573 if (
f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
578 auto prevNode = inner;
579 std::tie(currNode, currNodeID) =
descend(inner, currNodeID, branch, filter);
581 if (currNode !=
nullptr)
586 if (!newNode || childHash != newNode->getHash())
588 JLOG(
journal_.warn()) <<
"Corrupt node received";
597 if (newNode->isLeaf())
599 auto const& actualKey =
604 if (expectedNodeID.getNodeID() != node.
getNodeID())
607 <<
"Leaf node position mismatch: "
608 <<
"expected=" << expectedNodeID.getNodeID() <<
", actual=" << node.
getNodeID();
624 if (currNodeID != node)
627 JLOG(
journal_.warn()) <<
"unable to hook node " << node;
628 JLOG(
journal_.info()) <<
" stuck at " << currNodeID;
630 <<
", walked to= " << currNodeID.
getDepth();
637 newNode = prevNode->canonicalizeChild(branch, std::move(newNode));
639 if (filter !=
nullptr)
642 newNode->serializeWithPrefix(s);
650 JLOG(
journal_.trace()) <<
"got node, already had it (late)";
662 while (!stack.
empty())
664 auto const [node, otherNode] = stack.
top();
667 if ((node ==
nullptr) || (otherNode ==
nullptr))
669 JLOG(
journal_.info()) <<
"unable to fetch node";
672 if (otherNode->getHash() != node->getHash())
674 JLOG(
journal_.warn()) <<
"node hash mismatch";
680 if (!otherNode->isLeaf())
684 if (nodePeek->key() != otherNodePeek->key())
686 if (nodePeek->slice() != otherNodePeek->slice())
689 else if (node->isInner())
691 if (!otherNode->isInner())
695 for (
int i = 0; i < 16; ++i)
697 if (nodeInner->isEmptyBranch(i))
699 if (!otherInner->isEmptyBranch(i))
704 if (otherInner->isEmptyBranch(i))
707 auto next =
descend(nodeInner, i);
708 auto otherNext = other.
descend(otherInner, i);
709 if ((next ==
nullptr) || (otherNext ==
nullptr))
711 JLOG(
journal_.warn()) <<
"unable to fetch inner node";
714 stack.
emplace(next, otherNext);
728 auto node =
root_.get();
735 if (inner->isEmptyBranch(branch))
742 return (node->isInner()) && (node->getHash() == targetNodeHash);
750 auto node =
root_.get();
753 if (!node->isInner())
754 return node->getHash() == targetNodeHash;
760 if (inner->isEmptyBranch(branch))
763 if (inner->getChildHash(branch) == targetNodeHash)
768 }
while (node->isInner());
782 JLOG(
journal_.debug()) <<
"no path to " << key;
786 if (
auto const& node = stack.
top().first; !node || node->isInner() ||
789 JLOG(
journal_.debug()) <<
"no path to " << key;
795 while (!stack.
empty())
798 stack.
top().first->serializeForWire(s);
803 JLOG(
journal_.debug()) <<
"getPath for key " << key <<
", path length " <<
path.size();
810 if (
path.empty() ||
path.size() > 65)
816 for (
auto rit =
path.rbegin(); rit !=
path.rend(); ++rit)
818 auto const& blob = *rit;
823 if (node->getHash() != hash)
836 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.
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)
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
SHAMapAddNode addKnownNode(SHAMapNodeID const &nodeID, Slice const &rawNode, SHAMapSyncFilter const *filter)
std::stack< std::pair< SHAMapTreeNodePtr, SHAMapNodeID > > SharedPtrNodeStack
void canonicalize(SHAMapHash const &hash, SHAMapTreeNodePtr &) const
bool hasLeafNode(uint256 const &tag, SHAMapHash const &hash) const
Does this map have this leaf node?
bool getNodeFat(SHAMapNodeID const &wanted, std::vector< std::pair< SHAMapNodeID, Blob > > &data, bool fatLeaves, std::uint32_t depth) const
SHAMapAddNode addRootNode(SHAMapHash const &hash, Slice const &rootNode, SHAMapSyncFilter const *filter)
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.
An immutable linear range of bytes.
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
std::enable_if_t< std::is_integral_v< Integral > &&detail::is_engine< Engine >::value, Integral > randInt(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
std::enable_if_t< std::is_integral_v< Integral >, Integral > randInt()
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::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
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