1#include <xrpl/shamap/SHAMap.h>
3#include <xrpl/basics/IntrusivePointer.h>
4#include <xrpl/basics/IntrusivePointer.ipp>
5#include <xrpl/basics/Log.h>
6#include <xrpl/basics/SHAMapHash.h>
7#include <xrpl/basics/Slice.h>
8#include <xrpl/basics/TaggedCache.ipp>
9#include <xrpl/basics/base_uint.h>
10#include <xrpl/basics/contract.h>
11#include <xrpl/basics/safe_cast.h>
12#include <xrpl/beast/utility/instrumentation.h>
13#include <xrpl/nodestore/NodeObject.h>
14#include <xrpl/protocol/Serializer.h>
15#include <xrpl/shamap/Family.h>
16#include <xrpl/shamap/SHAMapAccountStateLeafNode.h>
17#include <xrpl/shamap/SHAMapInnerNode.h>
18#include <xrpl/shamap/SHAMapItem.h>
19#include <xrpl/shamap/SHAMapLeafNode.h>
20#include <xrpl/shamap/SHAMapMissingNode.h>
21#include <xrpl/shamap/SHAMapNodeID.h>
22#include <xrpl/shamap/SHAMapSyncFilter.h>
23#include <xrpl/shamap/SHAMapTreeNode.h>
24#include <xrpl/shamap/SHAMapTxLeafNode.h>
25#include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h>
27#include <boost/smart_ptr/intrusive_ptr.hpp>
56 "Attempt to create leaf node of unknown type " +
109 "xrpl::SHAMap::dirtyUp : valid state");
110 XRPL_ASSERT(child && (child->cowid() ==
cowid_),
"xrpl::SHAMap::dirtyUp : valid child input");
112 while (!stack.
empty())
117 XRPL_ASSERT(node,
"xrpl::SHAMap::dirtyUp : non-null node");
120 XRPL_ASSERT(branch >= 0,
"xrpl::SHAMap::dirtyUp : valid branch");
123 node->setChild(branch, std::move(child));
125 child = std::move(node);
133 stack ==
nullptr || stack->
empty(),
"xrpl::SHAMap::walkTowardsKey : empty stack input");
137 while (inNode->isInner())
139 if (stack !=
nullptr)
140 stack->
emplace(inNode, nodeID);
144 if (inner->isEmptyBranch(branch))
151 if (stack !=
nullptr)
152 stack->
emplace(inNode, nodeID);
160 if ((leaf !=
nullptr) && leaf->
peekItem()->key() !=
id)
168 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::fetchNodeFromDB : is backed");
176 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::finishFetch : is backed");
197 JLOG(
journal_.warn()) <<
"finishFetch exception: " << e.
what();
201 JLOG(
journal_.warn()) <<
"finishFetch exception: unknown exception: " << hash;
211 if (
auto nodeData = filter->
getNode(hash))
226 JLOG(
f_.journal().warn()) <<
"Invalid node/data, hash=" << hash <<
": " << x.
what();
251 if (filter !=
nullptr)
306 if ((ret !=
nullptr) || !
backed_)
350 XRPL_ASSERT(parent->
isInner(),
"xrpl::SHAMap::descend : valid parent input");
352 (branch >= 0) && (branch <
kBranchFactor),
"xrpl::SHAMap::descend : valid branch input");
354 !parent->
isEmptyBranch(branch),
"xrpl::SHAMap::descend : parent branch is non-empty");
358 if (child ==
nullptr)
366 child = childNode.
get();
392 if (filter !=
nullptr)
401 auto node = finishFetch(hash, object);
420 XRPL_ASSERT(node->cowid() <=
cowid_,
"xrpl::SHAMap::unshareNode : node valid for cowid");
421 if (node->cowid() !=
cowid_)
439 auto& [init, cmp, incr] = loopParams;
453 stack.
emplace(inner, stack.
top().second.getChildNodeID(branch));
455 for (
int i = init; cmp(i);)
457 if (!inner->isEmptyBranch(i))
460 XRPL_ASSERT(!stack.
empty(),
"xrpl::SHAMap::belowHelper : non-empty stack");
468 stack.
emplace(inner, stack.
top().second.getChildNodeID(branch));
482 auto cmp = [](
int i) {
return i >= 0; };
483 auto incr = [](
int& i) { --i; };
485 return belowHelper(node, stack, branch, {init, cmp, incr});
492 auto incr = [](
int& i) { ++i; };
494 return belowHelper(node, stack, branch, {init, cmp, incr});
496static boost::intrusive_ptr<SHAMapItem const>
const kNoItem;
498boost::intrusive_ptr<SHAMapItem const>
const&
509 if (!inner->isEmptyBranch(i))
511 if (nextNode !=
nullptr)
518 if (nextNode ==
nullptr)
521 UNREACHABLE(
"xrpl::SHAMap::onlyBelow : no next node");
533 leaf->peekItem() || (leaf ==
root_.get()),
"xrpl::SHAMap::onlyBelow : valid inner node");
534 return leaf->peekItem();
540 XRPL_ASSERT(stack.
empty(),
"xrpl::SHAMap::peekFirstItem : empty stack input");
544 while (!stack.
empty())
554 XRPL_ASSERT(!stack.
empty(),
"xrpl::SHAMap::peekNextItem : non-empty stack input");
555 XRPL_ASSERT(stack.
top().first->isLeaf(),
"xrpl::SHAMap::peekNextItem : stack starts with leaf");
557 while (!stack.
empty())
559 auto [node, nodeID] = stack.
top();
560 XRPL_ASSERT(!node->isLeaf(),
"xrpl::SHAMap::peekNextItem : another node is not leaf");
564 if (!inner->isEmptyBranch(i))
570 XRPL_ASSERT(leaf->isLeaf(),
"xrpl::SHAMap::peekNextItem : leaf is valid");
580boost::intrusive_ptr<SHAMapItem const>
const&
591boost::intrusive_ptr<SHAMapItem const>
const&
608 while (!stack.
empty())
610 auto [node, nodeID] = stack.
top();
614 if (leaf->peekItem()->key() >
id)
615 return ConstIterator(
this, leaf->peekItem().get(), std::move(stack));
622 if (!inner->isEmptyBranch(branch))
628 return ConstIterator(
this, leaf->peekItem().get(), std::move(stack));
641 while (!stack.
empty())
643 auto [node, nodeID] = stack.
top();
647 if (leaf->peekItem()->key() <
id)
648 return ConstIterator(
this, leaf->peekItem().get(), std::move(stack));
653 for (
int branch =
selectBranch(nodeID,
id) - 1; branch >= 0; --branch)
655 if (!inner->isEmptyBranch(branch))
658 auto leaf =
lastBelow(node, stack, branch);
661 return ConstIterator(
this, leaf->peekItem().get(), std::move(stack));
674 return (
findKey(
id) !=
nullptr);
692 if (!leaf || (leaf->peekItem()->key() !=
id))
700 while (!stack.
empty())
712 "xrpl::SHAMap::delItem : prevNode should be nullptr after std::move");
718 int const bc = node->getBranchCount();
735 if (!node->isEmptyBranch(i))
746 prevNode = std::move(node);
752 prevNode = std::move(node);
767 uint256 const tag = item->key();
775 auto [node, nodeID] = stack.
top();
781 if (leaf->peekItem()->key() == tag)
791 inner->isEmptyBranch(branch),
"xrpl::SHAMap::addGiveItem : inner branch is empty");
799 auto otherItem = leaf->peekItem();
801 otherItem && (tag != otherItem->key()),
"xrpl::SHAMap::addGiveItem : non-null item");
805 unsigned int b1 = 0, b2 = 0;
813 nodeID = nodeID.getChildNodeID(b1);
818 XRPL_ASSERT(node->isInner(),
"xrpl::SHAMap::addGiveItem : node is inner");
838 auto hash =
root_->getHash();
841 const_cast<SHAMap&
>(*this).unshare();
842 hash =
root_->getHash();
851 uint256 const tag = item->key();
862 auto nodeID = stack.
top().second;
865 if (!node || (node->peekItem()->key() != tag))
868 UNREACHABLE(
"xrpl::SHAMap::updateGiveItem : invalid node");
873 if (node->getType() != type)
875 JLOG(
journal_.fatal()) <<
"SHAMap::updateGiveItem: cross-type change!";
881 if (node->setItem(item))
890 if (hash ==
root_->getHash())
897 stream <<
"Fetch root TXN node " << hash;
901 stream <<
"Fetch root STATE node " << hash;
905 stream <<
"Fetch root SHAMap node " << hash;
914 XRPL_ASSERT(
root_->getHash() == hash,
"xrpl::SHAMap::fetchRoot : root hash do match");
936 XRPL_ASSERT(node->cowid() == 0,
"xrpl::SHAMap::writeNode : valid input node");
937 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::writeNode : is backed");
942 node->serializeWithPrefix(s);
956 XRPL_ASSERT(node->cowid(),
"xrpl::SHAMap::preFlushNode : valid input node");
958 if (node->cowid() !=
cowid_)
984 XRPL_ASSERT(!doWrite ||
backed_,
"xrpl::SHAMap::walkSubTree : valid input");
1005 if (node->isEmpty())
1025 if (node->isEmptyBranch(pos))
1033 int const branch = pos;
1034 auto child = node->getChild(pos++);
1036 if (child && (child->cowid() != 0))
1042 if (child->isInner())
1046 stack.
emplace(std::move(node), branch);
1057 "xrpl::SHAMap::walkSubTree : node cowid do "
1059 child->updateHash();
1065 node->shareChild(branch, child);
1072 node->updateHashDeep();
1085 auto parent = std::move(stack.
top().first);
1086 pos = stack.
top().second;
1090 XRPL_ASSERT(parent->cowid() ==
cowid_,
"xrpl::SHAMap::walkSubTree : parent cowid do match");
1091 parent->shareChild(pos, node);
1094 node = std::move(parent);
1099 root_ = std::move(node);
1108 JLOG(
journal_.info()) <<
" MAP Contains";
1115 auto [node, nodeID] = stack.
top();
1118 JLOG(
journal_.info()) << node->getString(nodeID);
1121 JLOG(
journal_.info()) <<
"Hash: " << node->getHash();
1124 if (node->isInner())
1129 if (!inner->isEmptyBranch(i))
1131 auto child = inner->getChildPointer(i);
1132 if (child !=
nullptr)
1135 child->getHash() == inner->getChildHash(i),
1136 "xrpl::SHAMap::dump : child hash do match");
1137 stack.
emplace(child, nodeID.getChildNodeID(i));
1146 }
while (!stack.
empty());
1148 JLOG(
journal_.info()) << leafCount <<
" resident leaves";
1154 auto ret =
f_.getTreeNodeCache()->fetch(hash.
asUInt256());
1155 XRPL_ASSERT(!ret || !ret->cowid(),
"xrpl::SHAMap::cacheLookup : not found or zero cowid");
1162 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::canonicalize : is backed");
1163 XRPL_ASSERT(node->cowid() == 0,
"xrpl::SHAMap::canonicalize : valid node input");
1164 XRPL_ASSERT(node->getHash() == hash,
"xrpl::SHAMap::canonicalize : node hash do match");
1166 f_.getTreeNodeCache()->canonicalizeReplaceClient(hash.
asUInt256(), node);
1173 auto node =
root_.get();
1174 XRPL_ASSERT(node,
"xrpl::SHAMap::invariants : non-null root node");
1175 XRPL_ASSERT(!node->isLeaf(),
"xrpl::SHAMap::invariants : root node is not leaf");
1180 node->invariants(
true);
uint256 const & asUInt256() const
SHAMapHash const & getChildHash(int m) const
SHAMapTreeNodePtr getChild(int branch)
SHAMapTreeNodePtr canonicalizeChild(int branch, SHAMapTreeNodePtr node)
SHAMapTreeNode * getChildPointer(int branch)
bool isInner() const override
Determines if this is an inner node.
bool isEmptyBranch(int m) const
boost::intrusive_ptr< SHAMapItem const > const & peekItem() const
Identifies a node inside a SHAMap.
SHAMapNodeID getChildNodeID(unsigned int m) const
virtual std::optional< Blob > getNode(SHAMapHash const &nodeHash) const =0
virtual void gotNode(bool fromFilter, SHAMapHash const &nodeHash, std::uint32_t ledgerSeq, Blob &&nodeData, SHAMapNodeType type) const =0
static SHAMapTreeNodePtr makeFromPrefix(Slice rawNode, SHAMapHash const &hash)
SHAMapHash const & getHash() const
Return the hash of this node.
virtual bool isLeaf() const =0
Determines if this is a leaf node.
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter const *filter)
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
ConstIterator lowerBound(uint256 const &id) const
Find the object with the greatest object id smaller than the input id.
SHAMapLeafNode * belowHelper(SHAMapTreeNodePtr node, SharedPtrNodeStack &stack, int branch, std::tuple< int, std::function< bool(int)>, std::function< void(int &)> > const &loopParams) const
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
SHAMapTreeNodePtr finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
static constexpr unsigned int kLeafDepth
The depth of the hash map: data is only present in the leaves.
SHAMapTreeNodePtr fetchNodeFromDB(SHAMapHash const &hash) const
SHAMapTreeNodePtr cacheLookup(SHAMapHash const &hash) const
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
int flushDirty(NodeObjectType t)
Flush modified nodes to the nodestore and convert them to shared.
static constexpr unsigned int kBranchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map).
SHAMapTreeNodePtr fetchNode(SHAMapHash const &hash) const
SHAMapLeafNode * firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack &stack, int branch=0) const
SHAMapLeafNode const * peekNextItem(uint256 const &id, SharedPtrNodeStack &stack) const
void dump(bool withHashes=false) const
SHAMapLeafNode * lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack &stack, int branch=kBranchFactor) const
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
bool hasItem(uint256 const &id) const
Does the tree have an item with the given ID?
int unshare()
Convert any modified nodes to shared.
bool updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter const *filter, bool &pending, descendCallback &&) const
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, SHAMapTreeNodePtr terminal)
Update hashes up to the root.
std::stack< std::pair< SHAMapTreeNodePtr, SHAMapNodeID > > SharedPtrNodeStack
ConstIterator upperBound(uint256 const &id) const
Find the first item after the given item.
void canonicalize(SHAMapHash const &hash, SHAMapTreeNodePtr &) const
SHAMapTreeNodePtr fetchNodeNT(SHAMapHash const &hash) const
SHAMapTreeNodePtr descendNoStore(SHAMapInnerNode &, int branch) const
SHAMapTreeNodePtr checkFilter(SHAMapHash const &hash, SHAMapSyncFilter const *filter) const
SHAMapLeafNode * findKey(uint256 const &id) const
Return nullptr if key not found.
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
ConstIterator end() const
SHAMapTreeNodePtr writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const
write and canonicalize modified node
std::function< void(SHAMapTreeNodePtr, SHAMapHash const &)> descendCallback
intr_ptr::SharedPtr< Node > preFlushNode(intr_ptr::SharedPtr< Node > node) const
prepare a node to be modified before flushing
intr_ptr::SharedPtr< Node > unshareNode(intr_ptr::SharedPtr< Node >, SHAMapNodeID const &nodeID)
Unshare the node, allowing it to be modified.
boost::intrusive_ptr< SHAMapItem const > const & onlyBelow(SHAMapTreeNode *) const
If there is only one leaf below this node, get its contents.
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
int walkSubTree(bool doWrite, NodeObjectType t)
SHAMapHash getHash() const
bool delItem(uint256 const &id)
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
T * get() const
Get the raw pointer.
void adopt(T *p)
Adopt the raw pointer.
SharedPtr< T > dynamicPointerCast(TT const &v)
SharedPtr< T > staticPointerCast(TT const &v)
SharedIntrusive< T > SharedPtr
SharedPtr< T > makeShared(A &&... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
intr_ptr::SharedPtr< SHAMapTreeNode > SHAMapTreeNodePtr
Dest safeDowncast(Src *s) noexcept
NodeObjectType
The types of node objects.
void logicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
intr_ptr::SharedPtr< SHAMapLeafNode > makeTypedLeaf(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item, std::uint32_t owner)
unsigned int selectBranch(SHAMapNodeID const &id, uint256 const &hash)
Returns the branch that would contain the given hash.
static boost::intrusive_ptr< SHAMapItem const > const kNoItem
SHAMapState
Describes the current state of a given SHAMap.
@ Immutable
The map is set in stone and cannot be changed.
@ Synching
The map's hash is fixed but valid nodes may be missing and can be added.
@ Modifying
The map is in flux and objects can be added and removed.
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
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)