1#include <xrpl/basics/TaggedCache.ipp>
2#include <xrpl/basics/contract.h>
3#include <xrpl/basics/safe_cast.h>
4#include <xrpl/shamap/SHAMap.h>
5#include <xrpl/shamap/SHAMapAccountStateLeafNode.h>
6#include <xrpl/shamap/SHAMapNodeID.h>
7#include <xrpl/shamap/SHAMapSyncFilter.h>
8#include <xrpl/shamap/SHAMapTxLeafNode.h>
9#include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h>
13[[nodiscard]] intr_ptr::SharedPtr<SHAMapLeafNode>
17 return intr_ptr::make_shared<SHAMapTxLeafNode>(std::move(item), owner);
20 return intr_ptr::make_shared<SHAMapTxPlusMetaLeafNode>(std::move(item), owner);
23 return intr_ptr::make_shared<SHAMapAccountStateLeafNode>(std::move(item), owner);
26 "Attempt to create leaf node of unknown type " +
33 root_ = intr_ptr::make_shared<SHAMapInnerNode>(
cowid_);
43 root_ = intr_ptr::make_shared<SHAMapInnerNode>(
cowid_);
48 , journal_(other.f_.journal())
49 , cowid_(other.cowid_ + 1)
50 , ledgerSeq_(other.ledgerSeq_)
54 , backed_(other.backed_)
82 "xrpl::SHAMap::dirtyUp : valid state");
83 XRPL_ASSERT(child && (child->cowid() ==
cowid_),
"xrpl::SHAMap::dirtyUp : valid child input");
85 while (!stack.
empty())
87 auto node = intr_ptr::dynamic_pointer_cast<SHAMapInnerNode>(stack.
top().
first);
90 XRPL_ASSERT(node,
"xrpl::SHAMap::dirtyUp : non-null node");
93 XRPL_ASSERT(branch >= 0,
"xrpl::SHAMap::dirtyUp : valid branch");
96 node->setChild(branch, std::move(child));
98 child = std::move(node);
106 stack ==
nullptr || stack->
empty(),
"xrpl::SHAMap::walkTowardsKey : empty stack input");
110 while (inNode->isInner())
112 if (stack !=
nullptr)
113 stack->
push({inNode, nodeID});
115 auto const inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(inNode);
117 if (inner->isEmptyBranch(branch))
124 if (stack !=
nullptr)
125 stack->
push({inNode, nodeID});
126 return safe_downcast<SHAMapLeafNode*>(inNode.get());
133 if ((leaf !=
nullptr) && leaf->
peekItem()->key() !=
id)
141 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::fetchNodeFromDB : is backed");
149 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::finishFetch : is backed");
174 JLOG(
journal_.
warn()) <<
"finishFetch exception: unknown exception: " << hash;
184 if (
auto nodeData = filter->
getNode(hash))
199 JLOG(
f_.
journal().
warn()) <<
"Invalid node/data, hash=" << hash <<
": " << x.
what();
224 if (filter !=
nullptr)
248 Throw<SHAMapMissingNode>(
type_, hash);
279 if ((ret !=
nullptr) || !
backed_)
323 XRPL_ASSERT(parent->
isInner(),
"xrpl::SHAMap::descend : valid parent input");
325 (branch >= 0) && (branch <
branchFactor),
"xrpl::SHAMap::descend : valid branch input");
327 !parent->
isEmptyBranch(branch),
"xrpl::SHAMap::descend : parent branch is non-empty");
331 if (child ==
nullptr)
339 child = childNode.
get();
365 if (filter !=
nullptr)
374 auto node = finishFetch(hash, object);
393 XRPL_ASSERT(node->cowid() <=
cowid_,
"xrpl::SHAMap::unshareNode : node valid for cowid");
394 if (node->cowid() !=
cowid_)
398 node = intr_ptr::static_pointer_cast<Node>(node->clone(
cowid_));
412 auto& [init, cmp, incr] = loopParams;
415 auto n = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
419 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
426 stack.
push({inner, stack.
top().second.getChildNodeID(branch)});
428 for (
int i = init; cmp(i);)
430 if (!inner->isEmptyBranch(i))
433 XRPL_ASSERT(!stack.
empty(),
"xrpl::SHAMap::belowHelper : non-empty stack");
436 auto n = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
440 inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
441 stack.
push({inner, stack.
top().second.getChildNodeID(branch)});
456 auto cmp = [](
int i) {
return i >= 0; };
457 auto incr = [](
int& i) { --i; };
459 return belowHelper(node, stack, branch, {init, cmp, incr});
467 auto incr = [](
int& i) { ++i; };
469 return belowHelper(node, stack, branch, {init, cmp, incr});
471static boost::intrusive_ptr<SHAMapItem const>
const no_item;
473boost::intrusive_ptr<SHAMapItem const>
const&
481 auto inner = safe_downcast<SHAMapInnerNode*>(node);
484 if (!inner->isEmptyBranch(i))
486 if (nextNode !=
nullptr)
493 if (nextNode ==
nullptr)
496 UNREACHABLE(
"xrpl::SHAMap::onlyBelow : no next node");
506 auto const leaf = safe_downcast<SHAMapLeafNode const*>(node);
508 leaf->peekItem() || (leaf ==
root_.get()),
"xrpl::SHAMap::onlyBelow : valid inner node");
509 return leaf->peekItem();
515 XRPL_ASSERT(stack.
empty(),
"xrpl::SHAMap::peekFirstItem : empty stack input");
519 while (!stack.
empty())
529 XRPL_ASSERT(!stack.
empty(),
"xrpl::SHAMap::peekNextItem : non-empty stack input");
530 XRPL_ASSERT(stack.
top().
first->isLeaf(),
"xrpl::SHAMap::peekNextItem : stack starts with leaf");
532 while (!stack.
empty())
534 auto [node, nodeID] = stack.
top();
535 XRPL_ASSERT(!node->isLeaf(),
"xrpl::SHAMap::peekNextItem : another node is not leaf");
536 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
539 if (!inner->isEmptyBranch(i))
544 Throw<SHAMapMissingNode>(
type_,
id);
545 XRPL_ASSERT(leaf->isLeaf(),
"xrpl::SHAMap::peekNextItem : leaf is valid");
555boost::intrusive_ptr<SHAMapItem const>
const&
566boost::intrusive_ptr<SHAMapItem const>
const&
583 while (!stack.
empty())
585 auto [node, nodeID] = stack.
top();
588 auto leaf = safe_downcast<SHAMapLeafNode*>(node.
get());
589 if (leaf->peekItem()->key() >
id)
590 return const_iterator(
this, leaf->peekItem().get(), std::move(stack));
594 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
597 if (!inner->isEmptyBranch(branch))
602 Throw<SHAMapMissingNode>(
type_,
id);
603 return const_iterator(
this, leaf->peekItem().get(), std::move(stack));
616 while (!stack.
empty())
618 auto [node, nodeID] = stack.
top();
621 auto leaf = safe_downcast<SHAMapLeafNode*>(node.
get());
622 if (leaf->peekItem()->key() <
id)
623 return const_iterator(
this, leaf->peekItem().get(), std::move(stack));
627 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
628 for (
int branch =
selectBranch(nodeID,
id) - 1; branch >= 0; --branch)
630 if (!inner->isEmptyBranch(branch))
633 auto leaf =
lastBelow(node, stack, branch);
635 Throw<SHAMapMissingNode>(
type_,
id);
636 return const_iterator(
this, leaf->peekItem().get(), std::move(stack));
649 return (
findKey(
id) !=
nullptr);
662 Throw<SHAMapMissingNode>(
type_,
id);
664 auto leaf = intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.
top().
first);
667 if (!leaf || (leaf->peekItem()->key() !=
id))
675 TreeNodeType prevNode;
677 while (!stack.
empty())
679 auto node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(stack.
top().
first);
689 "xrpl::SHAMap::delItem : prevNode should be nullptr after std::move");
695 int const bc = node->getBranchCount();
701 prevNode = TreeNodeType{};
712 if (!node->isEmptyBranch(i))
714 node->setChild(i, TreeNodeType{});
723 prevNode = std::move(node);
729 prevNode = std::move(node);
744 uint256 const tag = item->key();
750 Throw<SHAMapMissingNode>(
type_, tag);
752 auto [node, nodeID] = stack.
top();
757 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
758 if (leaf->peekItem()->key() == tag)
765 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
768 inner->isEmptyBranch(branch),
"xrpl::SHAMap::addGiveItem : inner branch is empty");
775 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
776 auto otherItem = leaf->peekItem();
778 otherItem && (tag != otherItem->key()),
"xrpl::SHAMap::addGiveItem : non-null item");
780 node = intr_ptr::make_shared<SHAMapInnerNode>(node->cowid());
782 unsigned int b1 = 0, b2 = 0;
786 stack.
push({node, nodeID});
790 nodeID = nodeID.getChildNodeID(b1);
791 node = intr_ptr::make_shared<SHAMapInnerNode>(
cowid_);
795 XRPL_ASSERT(node->isInner(),
"xrpl::SHAMap::addGiveItem : node is inner");
797 auto inner = safe_downcast<SHAMapInnerNode*>(node.
get());
815 auto hash =
root_->getHash();
819 hash =
root_->getHash();
828 uint256 const tag = item->key();
836 Throw<SHAMapMissingNode>(
type_, tag);
838 auto node = intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.
top().
first);
842 if (!node || (node->peekItem()->key() != tag))
845 UNREACHABLE(
"xrpl::SHAMap::updateGiveItem : invalid node");
850 if (node->getType() != type)
852 JLOG(
journal_.
fatal()) <<
"SHAMap::updateGiveItem: cross-type change!";
858 if (node->setItem(item))
867 if (hash ==
root_->getHash())
874 stream <<
"Fetch root TXN node " << hash;
878 stream <<
"Fetch root STATE node " << hash;
882 stream <<
"Fetch root SHAMap node " << hash;
891 XRPL_ASSERT(
root_->getHash() == hash,
"xrpl::SHAMap::fetchRoot : root hash do match");
913 XRPL_ASSERT(node->cowid() == 0,
"xrpl::SHAMap::writeNode : valid input node");
914 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::writeNode : is backed");
919 node->serializeWithPrefix(s);
933 XRPL_ASSERT(node->cowid(),
"xrpl::SHAMap::preFlushNode : valid input node");
935 if (node->cowid() !=
cowid_)
939 node = intr_ptr::static_pointer_cast<Node>(node->clone(
cowid_));
961 XRPL_ASSERT(!doWrite ||
backed_,
"xrpl::SHAMap::walkSubTree : valid input");
980 auto node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
root_);
984 root_ = intr_ptr::make_shared<SHAMapInnerNode>(0);
1002 if (node->isEmptyBranch(pos))
1010 int const branch = pos;
1011 auto child = node->getChild(pos++);
1013 if (child && (child->cowid() != 0))
1019 if (child->isInner())
1023 stack.
emplace(std::move(node), branch);
1024 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(child);
1034 "xrpl::SHAMap::walkSubTree : node cowid do "
1036 child->updateHash();
1042 node->shareChild(branch, child);
1049 node->updateHashDeep();
1055 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
writeNode(t, std::move(node)));
1062 auto parent = std::move(stack.
top().first);
1063 pos = stack.
top().second;
1067 XRPL_ASSERT(parent->cowid() ==
cowid_,
"xrpl::SHAMap::walkSubTree : parent cowid do match");
1068 parent->shareChild(pos, node);
1071 node = std::move(parent);
1076 root_ = std::move(node);
1092 auto [node, nodeID] = stack.
top();
1101 if (node->isInner())
1103 auto inner = safe_downcast<SHAMapInnerNode*>(node);
1106 if (!inner->isEmptyBranch(i))
1108 auto child = inner->getChildPointer(i);
1109 if (child !=
nullptr)
1112 child->getHash() == inner->getChildHash(i),
1113 "xrpl::SHAMap::dump : child hash do match");
1114 stack.
push({child, nodeID.getChildNodeID(i)});
1123 }
while (!stack.
empty());
1125 JLOG(
journal_.
info()) << leafCount <<
" resident leaves";
1132 XRPL_ASSERT(!ret || !ret->cowid(),
"xrpl::SHAMap::cacheLookup : not found or zero cowid");
1139 XRPL_ASSERT(
backed_,
"xrpl::SHAMap::canonicalize : is backed");
1140 XRPL_ASSERT(node->cowid() == 0,
"xrpl::SHAMap::canonicalize : valid node input");
1141 XRPL_ASSERT(node->getHash() == hash,
"xrpl::SHAMap::canonicalize : node hash do match");
1151 XRPL_ASSERT(node,
"xrpl::SHAMap::invariants : non-null root node");
1152 XRPL_ASSERT(!node->isLeaf(),
"xrpl::SHAMap::invariants : root node is not leaf");
1157 node->invariants(
true);
Stream trace() const
Severity stream access functions.
virtual beast::Journal const & journal()=0
virtual NodeStore::Database & db()=0
virtual void missingNodeAcquireBySeq(std::uint32_t refNum, uint256 const &nodeHash)=0
Acquire ledger that has a missing node by ledger sequence.
virtual std::shared_ptr< TreeNodeCache > getTreeNodeCache()=0
Return a pointer to the Family Tree Node Cache.
virtual void asyncFetch(uint256 const &hash, std::uint32_t ledgerSeq, std::function< void(std::shared_ptr< NodeObject > const &)> &&callback)
Fetch an object without waiting.
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous, bool duplicate=false)
Fetch a node object.
virtual void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t ledgerSeq)=0
Store the object.
uint256 const & as_uint256() const
SHAMapHash const & getChildHash(int m) const
SHAMapTreeNode * getChildPointer(int branch)
bool isInner() const override
Determines if this is an inner node.
intr_ptr::SharedPtr< SHAMapTreeNode > canonicalizeChild(int branch, intr_ptr::SharedPtr< SHAMapTreeNode > node)
intr_ptr::SharedPtr< SHAMapTreeNode > getChild(int branch)
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 intr_ptr::SharedPtr< SHAMapTreeNode > 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.
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
const_iterator upper_bound(uint256 const &id) const
Find the first item after the given item.
const_iterator lower_bound(uint256 const &id) const
Find the object with the greatest object id smaller than the input id.
intr_ptr::SharedPtr< SHAMapTreeNode > checkFilter(SHAMapHash const &hash, SHAMapSyncFilter *filter) const
const_iterator end() const
SHAMapLeafNode * firstBelow(intr_ptr::SharedPtr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) const
intr_ptr::SharedPtr< SHAMapTreeNode > writeNode(NodeObjectType t, intr_ptr::SharedPtr< SHAMapTreeNode > node) const
write and canonicalize modified node
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
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.
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeFromDB(SHAMapHash const &hash) const
SHAMapLeafNode * lastBelow(intr_ptr::SharedPtr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch=branchFactor) const
static constexpr unsigned int leafDepth
The depth of the hash map: data is only present in the leaves.
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNode(SHAMapHash const &hash) const
SHAMapLeafNode const * peekNextItem(uint256 const &id, SharedPtrNodeStack &stack) const
void dump(bool withHashes=false) const
SHAMapLeafNode * belowHelper(intr_ptr::SharedPtr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch, std::tuple< int, std::function< bool(int)>, std::function< void(int &)> > const &loopParams) const
static constexpr unsigned int branchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map)
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)
intr_ptr::SharedPtr< SHAMapTreeNode > finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeNT(SHAMapHash const &hash) const
void canonicalize(SHAMapHash const &hash, intr_ptr::SharedPtr< SHAMapTreeNode > &) const
SHAMapLeafNode * findKey(uint256 const &id) const
Return nullptr if key not found.
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
intr_ptr::SharedPtr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
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.
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, intr_ptr::SharedPtr< SHAMapTreeNode > terminal)
Update hashes up to the root.
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
intr_ptr::SharedPtr< SHAMapTreeNode > descendNoStore(SHAMapInnerNode &, int branch) const
int walkSubTree(bool doWrite, NodeObjectType t)
SHAMapHash getHash() const
bool delItem(uint256 const &id)
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
intr_ptr::SharedPtr< SHAMapTreeNode > root_
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
A shared intrusive pointer class that supports weak pointers.
T * get() const
Get the raw pointer.
void adopt(T *p)
Adopt the raw pointer.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
static boost::intrusive_ptr< SHAMapItem const > const no_item
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
@ pending
List will be valid in the future.
NodeObjectType
The types of node objects.
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.
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.
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)