20#include <xrpl/basics/TaggedCache.ipp> 
   21#include <xrpl/basics/contract.h> 
   22#include <xrpl/shamap/SHAMap.h> 
   23#include <xrpl/shamap/SHAMapAccountStateLeafNode.h> 
   24#include <xrpl/shamap/SHAMapNodeID.h> 
   25#include <xrpl/shamap/SHAMapSyncFilter.h> 
   26#include <xrpl/shamap/SHAMapTxLeafNode.h> 
   27#include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h> 
   31[[nodiscard]] intr_ptr::SharedPtr<SHAMapLeafNode>
 
   34    boost::intrusive_ptr<SHAMapItem const> item,
 
   38        return intr_ptr::make_shared<SHAMapTxLeafNode>(std::move(item), owner);
 
   41        return intr_ptr::make_shared<SHAMapTxPlusMetaLeafNode>(
 
   42            std::move(item), owner);
 
   45        return intr_ptr::make_shared<SHAMapAccountStateLeafNode>(
 
   46            std::move(item), owner);
 
   49        "Attempt to create leaf node of unknown type " +
 
 
   57    root_ = intr_ptr::make_shared<SHAMapInnerNode>(
cowid_);
 
 
   67    root_ = intr_ptr::make_shared<SHAMapInnerNode>(
cowid_);
 
 
   72    , journal_(other.f_.journal())
 
   73    , cowid_(other.cowid_ + 1)
 
   74    , ledgerSeq_(other.ledgerSeq_)
 
   78    , backed_(other.backed_)
 
 
  107        "ripple::SHAMap::dirtyUp : valid state");
 
  109        child && (child->cowid() == 
cowid_),
 
  110        "ripple::SHAMap::dirtyUp : valid child input");
 
  112    while (!stack.
empty())
 
  115            intr_ptr::dynamic_pointer_cast<SHAMapInnerNode>(stack.
top().
first);
 
  118        XRPL_ASSERT(node, 
"ripple::SHAMap::dirtyUp : non-null node");
 
  121        XRPL_ASSERT(branch >= 0, 
"ripple::SHAMap::dirtyUp : valid branch");
 
  124        node->setChild(branch, std::move(child));
 
  126        child = std::move(node);
 
 
  134        stack == 
nullptr || stack->
empty(),
 
  135        "ripple::SHAMap::walkTowardsKey : empty stack input");
 
  139    while (inNode->isInner())
 
  141        if (stack != 
nullptr)
 
  142            stack->
push({inNode, nodeID});
 
  145            intr_ptr::static_pointer_cast<SHAMapInnerNode>(inNode);
 
  147        if (inner->isEmptyBranch(branch))
 
  154    if (stack != 
nullptr)
 
  155        stack->
push({inNode, nodeID});
 
 
  163    if (leaf && leaf->
peekItem()->key() != 
id)
 
 
  171    XRPL_ASSERT(
backed_, 
"ripple::SHAMap::fetchNodeFromDB : is backed");
 
 
  181    XRPL_ASSERT(
backed_, 
"ripple::SHAMap::finishFetch : is backed");
 
  208            << 
"finishFetch exception: unknonw exception: " << hash;
 
 
  218    if (
auto nodeData = filter->
getNode(hash))
 
  230                    std::move(*nodeData),
 
  240                << 
"Invalid node/data, hash=" << hash << 
": " << x.
what();
 
 
  289        Throw<SHAMapMissingNode>(
type_, hash);
 
 
  366        parent->
isInner(), 
"ripple::SHAMap::descend : valid parent input");
 
  369        "ripple::SHAMap::descend : valid branch input");
 
  372        "ripple::SHAMap::descend : parent branch is non-empty");
 
  385            child = childNode.
get();
 
 
  419                [
this, hash, cb{std::move(callback)}](
 
  421                    auto node = finishFetch(hash, object);
 
 
  442        "ripple::SHAMap::unshareNode : node valid for cowid");
 
  443    if (node->cowid() != 
cowid_)
 
  448            "ripple::SHAMap::unshareNode : not immutable");
 
  449        node = intr_ptr::static_pointer_cast<Node>(node->clone(
cowid_));
 
 
  464    auto& [init, cmp, incr] = loopParams;
 
  467        auto n = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
 
  471    auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
 
  475        stack.
push({inner, stack.
top().second.getChildNodeID(branch)});
 
  476    for (
int i = init; cmp(i);)
 
  478        if (!inner->isEmptyBranch(i))
 
  483                "ripple::SHAMap::belowHelper : non-empty stack");
 
  486                auto n = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
 
  490            inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
 
  491            stack.
push({inner, stack.
top().second.getChildNodeID(branch)});
 
 
  506    auto cmp = [](
int i) { 
return i >= 0; };
 
  507    auto incr = [](
int& i) { --i; };
 
  509    return belowHelper(node, stack, branch, {init, cmp, incr});
 
 
  519    auto incr = [](
int& i) { ++i; };
 
  521    return belowHelper(node, stack, branch, {init, cmp, incr});
 
 
  523static boost::intrusive_ptr<SHAMapItem const> 
const no_item;
 
  525boost::intrusive_ptr<SHAMapItem const> 
const&
 
  536            if (!inner->isEmptyBranch(i))
 
  548            UNREACHABLE(
"ripple::SHAMap::onlyBelow : no next node");
 
  560        leaf->peekItem() || (leaf == 
root_.get()),
 
  561        "ripple::SHAMap::onlyBelow : valid inner node");
 
  562    return leaf->peekItem();
 
 
  569        stack.
empty(), 
"ripple::SHAMap::peekFirstItem : empty stack input");
 
  573        while (!stack.
empty())
 
 
  584        !stack.
empty(), 
"ripple::SHAMap::peekNextItem : non-empty stack input");
 
  587        "ripple::SHAMap::peekNextItem : stack starts with leaf");
 
  589    while (!stack.
empty())
 
  591        auto [node, nodeID] = stack.
top();
 
  594            "ripple::SHAMap::peekNextItem : another node is not leaf");
 
  595        auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
 
  598            if (!inner->isEmptyBranch(i))
 
  603                    Throw<SHAMapMissingNode>(
type_, 
id);
 
  606                    "ripple::SHAMap::peekNextItem : leaf is valid");
 
 
  616boost::intrusive_ptr<SHAMapItem const> 
const&
 
  627boost::intrusive_ptr<SHAMapItem const> 
const&
 
  644    while (!stack.
empty())
 
  646        auto [node, nodeID] = stack.
top();
 
  650            if (leaf->peekItem()->key() > id)
 
  652                    this, leaf->peekItem().get(), std::move(stack));
 
  656            auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
 
  661                if (!inner->isEmptyBranch(branch))
 
  666                        Throw<SHAMapMissingNode>(
type_, 
id);
 
  668                        this, leaf->peekItem().get(), std::move(stack));
 
 
  681    while (!stack.
empty())
 
  683        auto [node, nodeID] = stack.
top();
 
  687            if (leaf->peekItem()->key() < id)
 
  689                    this, leaf->peekItem().get(), std::move(stack));
 
  693            auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
 
  694            for (
int branch = 
selectBranch(nodeID, 
id) - 1; branch >= 0;
 
  697                if (!inner->isEmptyBranch(branch))
 
  700                    auto leaf = 
lastBelow(node, stack, branch);
 
  702                        Throw<SHAMapMissingNode>(
type_, 
id);
 
  704                        this, leaf->peekItem().get(), std::move(stack));
 
 
  717    return (
findKey(
id) != 
nullptr);
 
 
  726        "ripple::SHAMap::delItem : not immutable");
 
  732        Throw<SHAMapMissingNode>(
type_, 
id);
 
  735        intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.
top().
first);
 
  738    if (!leaf || (leaf->peekItem()->key() != 
id))
 
  747    while (!stack.
empty())
 
  750            intr_ptr::static_pointer_cast<SHAMapInnerNode>(stack.
top().
first);
 
  755        node->setChild(
selectBranch(nodeID, 
id), std::move(prevNode));
 
  761            int const bc = node->getBranchCount();
 
  776                        if (!node->isEmptyBranch(i))
 
  788                    prevNode = std::move(node);
 
  794                prevNode = std::move(node);
 
 
  805    boost::intrusive_ptr<SHAMapItem const> item)
 
  809        "ripple::SHAMap::addGiveItem : not immutable");
 
  812        "ripple::SHAMap::addGiveItem : valid type input");
 
  821        Throw<SHAMapMissingNode>(
type_, tag);
 
  823    auto [node, nodeID] = stack.
top();
 
  828        auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
 
  829        if (leaf->peekItem()->key() == tag)
 
  836        auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
 
  839            inner->isEmptyBranch(branch),
 
  840            "ripple::SHAMap::addGiveItem : inner branch is empty");
 
  847        auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
 
  848        auto otherItem = leaf->peekItem();
 
  850            otherItem && (tag != otherItem->key()),
 
  851            "ripple::SHAMap::addGiveItem : non-null item");
 
  853        node = intr_ptr::make_shared<SHAMapInnerNode>(node->cowid());
 
  860            stack.
push({node, nodeID});
 
  864            nodeID = nodeID.getChildNodeID(b1);
 
  865            node = intr_ptr::make_shared<SHAMapInnerNode>(
cowid_);
 
  870            node->isInner(), 
"ripple::SHAMap::addGiveItem : node is inner");
 
 
  884    boost::intrusive_ptr<SHAMapItem const> item)
 
 
  892    auto hash = 
root_->getHash();
 
  896        hash = 
root_->getHash();
 
 
  904    boost::intrusive_ptr<SHAMapItem const> item)
 
  911        "ripple::SHAMap::updateGiveItem : not immutable");
 
  917        Throw<SHAMapMissingNode>(
type_, tag);
 
  920        intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.
top().
first);
 
  924    if (!node || (node->peekItem()->key() != tag))
 
  927        UNREACHABLE(
"ripple::SHAMap::updateGiveItem : invalid node");
 
  932    if (node->getType() != type)
 
  934        JLOG(
journal_.
fatal()) << 
"SHAMap::updateGiveItem: cross-type change!";
 
  940    if (node->setItem(item))
 
 
  949    if (hash == 
root_->getHash())
 
  956            stream << 
"Fetch root TXN node " << hash;
 
  960            stream << 
"Fetch root STATE node " << hash;
 
  964            stream << 
"Fetch root SHAMap node " << hash;
 
  974            root_->getHash() == hash,
 
  975            "ripple::SHAMap::fetchRoot : root hash do match");
 
 
  999        node->cowid() == 0, 
"ripple::SHAMap::writeNode : valid input node");
 
 1000    XRPL_ASSERT(
backed_, 
"ripple::SHAMap::writeNode : is backed");
 
 1005    node->serializeWithPrefix(s);
 
 
 1014template <
class Node>
 
 1021        node->cowid(), 
"ripple::SHAMap::preFlushNode : valid input node");
 
 1023    if (node->cowid() != 
cowid_)
 
 1027        node = intr_ptr::static_pointer_cast<Node>(node->clone(
cowid_));
 
 
 1050        !doWrite || 
backed_, 
"ripple::SHAMap::walkSubTree : valid input");
 
 1057    if (
root_->isLeaf())
 
 1060        root_->updateHash();
 
 1069    auto node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
root_);
 
 1071    if (node->isEmpty())
 
 1073        root_ = intr_ptr::make_shared<SHAMapInnerNode>(0);
 
 1091            if (node->isEmptyBranch(pos))
 
 1100                auto child = node->getChild(pos++);
 
 1102                if (child && (child->cowid() != 0))
 
 1108                    if (child->isInner())
 
 1112                        stack.
emplace(std::move(node), branch);
 
 1116                        node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
 
 1127                            "ripple::SHAMap::walkSubTree : node cowid do " 
 1129                        child->updateHash();
 
 1135                        node->shareChild(branch, child);
 
 1142        node->updateHashDeep();
 
 1148            node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
 
 1156        auto parent = std::move(stack.
top().first);
 
 1157        pos = stack.
top().second;
 
 1162            parent->cowid() == 
cowid_,
 
 1163            "ripple::SHAMap::walkSubTree : parent cowid do match");
 
 1164        parent->shareChild(pos, node);
 
 1167        node = std::move(parent);
 
 1172    root_ = std::move(node);
 
 
 1188        auto [node, nodeID] = stack.
top();
 
 1197        if (node->isInner())
 
 1202                if (!inner->isEmptyBranch(i))
 
 1204                    auto child = inner->getChildPointer(i);
 
 1208                            child->getHash() == inner->getChildHash(i),
 
 1209                            "ripple::SHAMap::dump : child hash do match");
 
 1210                        stack.
push({child, nodeID.getChildNodeID(i)});
 
 1217    } 
while (!stack.
empty());
 
 1219    JLOG(
journal_.
info()) << leafCount << 
" resident leaves";
 
 
 1227        !ret || !ret->cowid(),
 
 1228        "ripple::SHAMap::cacheLookup : not found or zero cowid");
 
 
 1237    XRPL_ASSERT(
backed_, 
"ripple::SHAMap::canonicalize : is backed");
 
 1239        node->cowid() == 0, 
"ripple::SHAMap::canonicalize : valid node input");
 
 1241        node->getHash() == hash,
 
 1242        "ripple::SHAMap::canonicalize : node hash do match");
 
 
 1252    XRPL_ASSERT(node, 
"ripple::SHAMap::invariants : non-null root node");
 
 1254        !node->isLeaf(), 
"ripple::SHAMap::invariants : root node is not leaf");
 
 1259    node->invariants(
true);
 
 
Stream trace() const
Severity stream access functions.
 
virtual beast::Journal const & journal()=0
 
virtual std::shared_ptr< TreeNodeCache > getTreeNodeCache()=0
Return a pointer to the Family Tree Node Cache.
 
virtual void missingNodeAcquireBySeq(std::uint32_t refNum, uint256 const &nodeHash)=0
Acquire ledger that has a missing node by ledger sequence.
 
virtual NodeStore::Database & db()=0
 
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
 
bool isInner() const override
Determines if this is an inner node.
 
void setChild(int m, intr_ptr::SharedPtr< SHAMapTreeNode > child)
 
bool isEmptyBranch(int m) const
 
intr_ptr::SharedPtr< SHAMapTreeNode > getChild(int branch)
 
SHAMapHash const & getChildHash(int m) const
 
intr_ptr::SharedPtr< SHAMapTreeNode > canonicalizeChild(int branch, intr_ptr::SharedPtr< SHAMapTreeNode > node)
 
SHAMapTreeNode * getChildPointer(int branch)
 
boost::intrusive_ptr< SHAMapItem const > const & peekItem() const
 
Identifies a node inside a SHAMap.
 
SHAMapNodeID getChildNodeID(unsigned int m) const
 
virtual void gotNode(bool fromFilter, SHAMapHash const &nodeHash, std::uint32_t ledgerSeq, Blob &&nodeData, SHAMapNodeType type) const =0
 
virtual std::optional< Blob > getNode(SHAMapHash const &nodeHash) const =0
 
virtual bool isLeaf() const =0
Determines if this is a leaf node.
 
static intr_ptr::SharedPtr< SHAMapTreeNode > makeFromPrefix(Slice rawNode, SHAMapHash const &hash)
 
SHAMapHash const & getHash() const
Return the hash of this node.
 
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
 
intr_ptr::SharedPtr< Node > preFlushNode(intr_ptr::SharedPtr< Node > node) const
prepare a node to be modified before flushing
 
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
 
bool hasItem(uint256 const &id) const
Does the tree have an item with the given ID?
 
intr_ptr::SharedPtr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
 
intr_ptr::SharedPtr< Node > unshareNode(intr_ptr::SharedPtr< Node >, SHAMapNodeID const &nodeID)
Unshare the node, allowing it to be modified.
 
static constexpr unsigned int leafDepth
The depth of the hash map: data is only present in the leaves.
 
void dump(bool withHashes=false) const
 
boost::intrusive_ptr< SHAMapItem const > const & onlyBelow(SHAMapTreeNode *) const
If there is only one leaf below this node, get its contents.
 
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
 
intr_ptr::SharedPtr< SHAMapTreeNode > root_
 
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
 
intr_ptr::SharedPtr< SHAMapTreeNode > finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
 
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
 
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
 
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
 
SHAMapLeafNode const * peekNextItem(uint256 const &id, SharedPtrNodeStack &stack) const
 
void canonicalize(SHAMapHash const &hash, intr_ptr::SharedPtr< SHAMapTreeNode > &) const
 
int walkSubTree(bool doWrite, NodeObjectType t)
 
const_iterator end() const
 
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.
 
intr_ptr::SharedPtr< SHAMapTreeNode > writeNode(NodeObjectType t, intr_ptr::SharedPtr< SHAMapTreeNode > node) const
write and canonicalize modified node
 
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeNT(SHAMapHash const &hash) const
 
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
 
SHAMapHash getHash() const
 
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, intr_ptr::SharedPtr< SHAMapTreeNode > terminal)
Update hashes up to the root.
 
bool updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
 
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
 
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNode(SHAMapHash const &hash) const
 
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeFromDB(SHAMapHash const &hash) const
 
intr_ptr::SharedPtr< SHAMapTreeNode > descendNoStore(SHAMapInnerNode &, int branch) const
 
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
 
bool delItem(uint256 const &id)
 
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
 
const_iterator lower_bound(uint256 const &id) const
Find the object with the greatest object id smaller than the input id.
 
SHAMapLeafNode * lastBelow(intr_ptr::SharedPtr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch=branchFactor) const
 
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
 
int flushDirty(NodeObjectType t)
Flush modified nodes to the nodestore and convert them to shared.
 
int unshare()
Convert any modified nodes to shared.
 
intr_ptr::SharedPtr< SHAMapTreeNode > checkFilter(SHAMapHash const &hash, SHAMapSyncFilter *filter) 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
 
SHAMapLeafNode * findKey(uint256 const &id) const
Return nullptr if key not found.
 
static constexpr unsigned int branchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map)
 
SHAMapLeafNode * firstBelow(intr_ptr::SharedPtr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) const
 
A shared intrusive pointer class that supports weak pointers.
 
void adopt(T *p)
Adopt the raw pointer.
 
T * get() const
Get the raw pointer.
 
void reset()
Set the pointer to null, decrement the strong count, and run the appropriate release action.
 
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
 
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.
 
intr_ptr::SharedPtr< SHAMapLeafNode > makeTypedLeaf(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item, std::uint32_t owner)
 
NodeObjectType
The types of node objects.
 
@ pending
List will be valid in the future.
 
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)
 
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 no_item
 
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.