20#include <xrpld/shamap/SHAMap.h>
21#include <xrpld/shamap/SHAMapAccountStateLeafNode.h>
22#include <xrpld/shamap/SHAMapNodeID.h>
23#include <xrpld/shamap/SHAMapSyncFilter.h>
24#include <xrpld/shamap/SHAMapTxLeafNode.h>
25#include <xrpld/shamap/SHAMapTxPlusMetaLeafNode.h>
27#include <xrpl/basics/contract.h>
34 boost::intrusive_ptr<SHAMapItem const> item,
38 return std::make_shared<SHAMapTxLeafNode>(std::move(item), owner);
41 return std::make_shared<SHAMapTxPlusMetaLeafNode>(
42 std::move(item), owner);
45 return std::make_shared<SHAMapAccountStateLeafNode>(
46 std::move(item), owner);
49 "Attempt to create leaf node of unknown type " +
72 , journal_(other.f_.journal())
73 , cowid_(other.cowid_ + 1)
74 , ledgerSeq_(other.ledgerSeq_)
78 , backed_(other.backed_)
91 return std::make_shared<SHAMap>(*
this, isMutable);
107 "ripple::SHAMap::dirtyUp : valid state");
109 child && (child->cowid() ==
cowid_),
110 "ripple::SHAMap::dirtyUp : valid child input");
112 while (!stack.
empty())
115 std::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});
144 auto const inner = std::static_pointer_cast<SHAMapInnerNode>(inNode);
146 if (inner->isEmptyBranch(branch))
153 if (stack !=
nullptr)
154 stack->
push({inNode, nodeID});
162 if (leaf && leaf->
peekItem()->key() !=
id)
170 XRPL_ASSERT(
backed_,
"ripple::SHAMap::fetchNodeFromDB : is backed");
180 XRPL_ASSERT(
backed_,
"ripple::SHAMap::finishFetch : is backed");
207 <<
"finishFetch exception: unknonw exception: " << hash;
217 if (
auto nodeData = filter->
getNode(hash))
229 std::move(*nodeData),
239 <<
"Invalid node/data, hash=" << hash <<
": " << x.
what();
288 Throw<SHAMapMissingNode>(
type_, hash);
310 if (!ret && !parent->isEmptyBranch(branch))
311 Throw<SHAMapMissingNode>(
type_, parent->getChildHash(branch));
340 node =
fetchNode(parent->getChildHash(branch));
344 node = parent->canonicalizeChild(branch, std::move(node));
357 ret =
fetchNode(parent->getChildHash(branch));
369 parent->
isInner(),
"ripple::SHAMap::descend : valid parent input");
372 "ripple::SHAMap::descend : valid branch input");
375 "ripple::SHAMap::descend : parent branch is non-empty");
388 child = childNode.
get();
422 [
this, hash, cb{std::move(callback)}](
424 auto node = finishFetch(hash, object);
445 "ripple::SHAMap::unshareNode : node valid for cowid");
446 if (node->cowid() !=
cowid_)
451 "ripple::SHAMap::unshareNode : not immutable");
452 node = std::static_pointer_cast<Node>(node->clone(
cowid_));
467 auto& [init, cmp, incr] = loopParams;
470 auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
474 auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
478 stack.
push({inner, stack.
top().second.getChildNodeID(branch)});
479 for (
int i = init; cmp(i);)
481 if (!inner->isEmptyBranch(i))
486 "ripple::SHAMap::belowHelper : non-empty stack");
489 auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
493 inner = std::static_pointer_cast<SHAMapInnerNode>(node);
494 stack.
push({inner, stack.
top().second.getChildNodeID(branch)});
509 auto cmp = [](
int i) {
return i >= 0; };
510 auto incr = [](
int& i) { --i; };
512 return belowHelper(node, stack, branch, {init, cmp, incr});
522 auto incr = [](
int& i) { ++i; };
524 return belowHelper(node, stack, branch, {init, cmp, incr});
526static const boost::intrusive_ptr<SHAMapItem const>
no_item;
528boost::intrusive_ptr<SHAMapItem const>
const&
539 if (!inner->isEmptyBranch(i))
550 UNREACHABLE(
"ripple::SHAMap::onlyBelow : no next node");
561 leaf->peekItem() || (leaf ==
root_.get()),
562 "ripple::SHAMap::onlyBelow : valid inner node");
563 return leaf->peekItem();
570 stack.
empty(),
"ripple::SHAMap::peekFirstItem : empty stack input");
574 while (!stack.
empty())
585 !stack.
empty(),
"ripple::SHAMap::peekNextItem : non-empty stack input");
588 "ripple::SHAMap::peekNextItem : stack starts with leaf");
590 while (!stack.
empty())
592 auto [node, nodeID] = stack.
top();
595 "ripple::SHAMap::peekNextItem : another node is not leaf");
596 auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
599 if (!inner->isEmptyBranch(i))
604 Throw<SHAMapMissingNode>(
type_,
id);
607 "ripple::SHAMap::peekNextItem : leaf is valid");
617boost::intrusive_ptr<SHAMapItem const>
const&
628boost::intrusive_ptr<SHAMapItem const>
const&
645 while (!stack.
empty())
647 auto [node, nodeID] = stack.
top();
651 if (leaf->peekItem()->key() > id)
653 this, leaf->peekItem().get(), std::move(stack));
657 auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
662 if (!inner->isEmptyBranch(branch))
667 Throw<SHAMapMissingNode>(
type_,
id);
669 this, leaf->peekItem().get(), std::move(stack));
682 while (!stack.
empty())
684 auto [node, nodeID] = stack.
top();
688 if (leaf->peekItem()->key() < id)
690 this, leaf->peekItem().get(), std::move(stack));
694 auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
695 for (
int branch =
selectBranch(nodeID,
id) - 1; branch >= 0;
698 if (!inner->isEmptyBranch(branch))
701 auto leaf =
lastBelow(node, stack, branch);
703 Throw<SHAMapMissingNode>(
type_,
id);
705 this, leaf->peekItem().get(), std::move(stack));
718 return (
findKey(
id) !=
nullptr);
727 "ripple::SHAMap::delItem : not immutable");
733 Throw<SHAMapMissingNode>(
type_,
id);
735 auto leaf = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.
top().
first);
738 if (!leaf || (leaf->peekItem()->key() !=
id))
747 while (!stack.
empty())
750 std::static_pointer_cast<SHAMapInnerNode>(stack.
top().
first);
755 node->setChild(
selectBranch(nodeID,
id), std::move(prevNode));
761 const int bc = node->getBranchCount();
776 if (!node->isEmptyBranch(i))
778 node->setChild(i,
nullptr);
787 prevNode = std::move(node);
793 prevNode = std::move(node);
804 boost::intrusive_ptr<SHAMapItem const> item)
808 "ripple::SHAMap::addGiveItem : not immutable");
811 "ripple::SHAMap::addGiveItem : valid type input");
820 Throw<SHAMapMissingNode>(
type_, tag);
822 auto [node, nodeID] = stack.
top();
827 auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
828 if (leaf->peekItem()->key() == tag)
835 auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
838 inner->isEmptyBranch(branch),
839 "ripple::SHAMap::addGiveItem : inner branch is empty");
846 auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
847 auto otherItem = leaf->peekItem();
849 otherItem && (tag != otherItem->key()),
850 "ripple::SHAMap::addGiveItem : non-null item");
852 node = std::make_shared<SHAMapInnerNode>(node->cowid());
859 stack.
push({node, nodeID});
863 nodeID = nodeID.getChildNodeID(b1);
864 node = std::make_shared<SHAMapInnerNode>(
cowid_);
869 node->isInner(),
"ripple::SHAMap::addGiveItem : node is inner");
883 boost::intrusive_ptr<SHAMapItem const> item)
891 auto hash =
root_->getHash();
895 hash =
root_->getHash();
903 boost::intrusive_ptr<SHAMapItem const> item)
910 "ripple::SHAMap::updateGiveItem : not immutable");
916 Throw<SHAMapMissingNode>(
type_, tag);
918 auto node = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.
top().
first);
922 if (!node || (node->peekItem()->key() != tag))
924 UNREACHABLE(
"ripple::SHAMap::updateGiveItem : invalid node");
928 if (node->getType() != type)
930 JLOG(
journal_.
fatal()) <<
"SHAMap::updateGiveItem: cross-type change!";
936 if (node->setItem(item))
945 if (hash ==
root_->getHash())
952 stream <<
"Fetch root TXN node " << hash;
956 stream <<
"Fetch root STATE node " << hash;
960 stream <<
"Fetch root SHAMap node " << hash;
970 root_->getHash() == hash,
971 "ripple::SHAMap::fetchRoot : root hash do match");
994 node->cowid() == 0,
"ripple::SHAMap::writeNode : valid input node");
995 XRPL_ASSERT(
backed_,
"ripple::SHAMap::writeNode : is backed");
1000 node->serializeWithPrefix(s);
1009template <
class Node>
1016 node->cowid(),
"ripple::SHAMap::preFlushNode : valid input node");
1018 if (node->cowid() !=
cowid_)
1022 node = std::static_pointer_cast<Node>(node->clone(
cowid_));
1045 !doWrite ||
backed_,
"ripple::SHAMap::walkSubTree : valid input");
1052 if (
root_->isLeaf())
1055 root_->updateHash();
1064 auto node = std::static_pointer_cast<SHAMapInnerNode>(
root_);
1066 if (node->isEmpty())
1068 root_ = std::make_shared<SHAMapInnerNode>(0);
1086 if (node->isEmptyBranch(pos))
1095 auto child = node->getChild(pos++);
1097 if (child && (child->cowid() != 0))
1103 if (child->isInner())
1107 stack.
emplace(std::move(node), branch);
1111 node = std::static_pointer_cast<SHAMapInnerNode>(
1122 "ripple::SHAMap::walkSubTree : node cowid do "
1124 child->updateHash();
1130 node->shareChild(branch, child);
1137 node->updateHashDeep();
1143 node = std::static_pointer_cast<SHAMapInnerNode>(
1151 auto parent = std::move(stack.
top().first);
1152 pos = stack.
top().second;
1157 parent->cowid() ==
cowid_,
1158 "ripple::SHAMap::walkSubTree : parent cowid do match");
1159 parent->shareChild(pos, node);
1162 node = std::move(parent);
1167 root_ = std::move(node);
1183 auto [node, nodeID] = stack.
top();
1192 if (node->isInner())
1197 if (!inner->isEmptyBranch(i))
1199 auto child = inner->getChildPointer(i);
1203 child->getHash() == inner->getChildHash(i),
1204 "ripple::SHAMap::dump : child hash do match");
1205 stack.
push({child, nodeID.getChildNodeID(i)});
1212 }
while (!stack.
empty());
1214 JLOG(
journal_.
info()) << leafCount <<
" resident leaves";
1222 !ret || !ret->cowid(),
1223 "ripple::SHAMap::cacheLookup : not found or zero cowid");
1232 XRPL_ASSERT(
backed_,
"ripple::SHAMap::canonicalize : is backed");
1234 node->cowid() == 0,
"ripple::SHAMap::canonicalize : valid node input");
1236 node->getHash() == hash,
1237 "ripple::SHAMap::canonicalize : node hash do match");
1246 auto node =
root_.get();
1247 XRPL_ASSERT(node,
"ripple::SHAMap::invariants : non-null root node");
1249 !node->isLeaf(),
"ripple::SHAMap::invariants : root node is not leaf");
1254 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.
bool isEmptyBranch(int m) const
void setChild(int m, std::shared_ptr< SHAMapTreeNode > child)
SHAMapHash const & getChildHash(int m) const
std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< 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 std::shared_ptr< 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.
std::shared_ptr< SHAMapTreeNode > fetchNodeNT(SHAMapHash const &hash) const
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?
std::shared_ptr< SHAMapTreeNode > checkFilter(SHAMapHash const &hash, SHAMapSyncFilter *filter) const
static constexpr unsigned int leafDepth
The depth of the hash map: data is only present in the leaves.
void dump(bool withHashes=false) const
SHAMapLeafNode * firstBelow(std::shared_ptr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) 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
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, std::shared_ptr< SHAMapTreeNode > terminal)
Update hashes up to the root.
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
SHAMapLeafNode * belowHelper(std::shared_ptr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch, std::tuple< int, std::function< bool(int)>, std::function< void(int &)> > const &loopParams) const
SHAMapLeafNode * lastBelow(std::shared_ptr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch=branchFactor) const
std::shared_ptr< SHAMapTreeNode > writeNode(NodeObjectType t, std::shared_ptr< SHAMapTreeNode > node) const
write and canonicalize modified node
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
std::shared_ptr< Node > preFlushNode(std::shared_ptr< Node > node) const
prepare a node to be modified before flushing
std::shared_ptr< SHAMapTreeNode > root_
int walkSubTree(bool doWrite, NodeObjectType t)
std::shared_ptr< SHAMapTreeNode > fetchNode(SHAMapHash const &hash) const
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.
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
SHAMapHash getHash() const
bool updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
std::shared_ptr< SHAMapTreeNode > descendNoStore(std::shared_ptr< SHAMapInnerNode > const &, int branch) const
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
std::shared_ptr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
std::shared_ptr< SHAMapTreeNode > fetchNodeFromDB(SHAMapHash const &hash) const
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
void canonicalize(SHAMapHash const &hash, std::shared_ptr< SHAMapTreeNode > &) const
bool delItem(uint256 const &id)
std::shared_ptr< Node > unshareNode(std::shared_ptr< Node >, SHAMapNodeID const &nodeID)
Unshare the node, allowing it to be modified.
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.
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.
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)
std::shared_ptr< SHAMapTreeNode > finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
unsigned int selectBranch(SHAMapNodeID const &id, uint256 const &hash)
Returns the branch that would contain the given hash.
static const boost::intrusive_ptr< SHAMapItem const > no_item
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.
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)
std::shared_ptr< SHAMapLeafNode > makeTypedLeaf(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item, std::uint32_t owner)
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.