rippled
Loading...
Searching...
No Matches
SHAMap.cpp
1#include <xrpl/basics/TaggedCache.ipp>
2#include <xrpl/basics/contract.h>
3#include <xrpl/shamap/SHAMap.h>
4#include <xrpl/shamap/SHAMapAccountStateLeafNode.h>
5#include <xrpl/shamap/SHAMapNodeID.h>
6#include <xrpl/shamap/SHAMapSyncFilter.h>
7#include <xrpl/shamap/SHAMapTxLeafNode.h>
8#include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h>
9
10namespace xrpl {
11
12[[nodiscard]] intr_ptr::SharedPtr<SHAMapLeafNode>
14 SHAMapNodeType type,
15 boost::intrusive_ptr<SHAMapItem const> item,
16 std::uint32_t owner)
17{
19 return intr_ptr::make_shared<SHAMapTxLeafNode>(std::move(item), owner);
20
22 return intr_ptr::make_shared<SHAMapTxPlusMetaLeafNode>(
23 std::move(item), owner);
24
26 return intr_ptr::make_shared<SHAMapAccountStateLeafNode>(
27 std::move(item), owner);
28
30 "Attempt to create leaf node of unknown type " +
32 static_cast<std::underlying_type_t<SHAMapNodeType>>(type)));
33}
34
36 : f_(f), journal_(f.journal()), state_(SHAMapState::Modifying), type_(t)
37{
38 root_ = intr_ptr::make_shared<SHAMapInnerNode>(cowid_);
39}
40
41// The `hash` parameter is unused. It is part of the interface so it's clear
42// from the parameters that this is the constructor to use when the hash is
43// known. The fact that the parameter is unused is an implementation detail that
44// should not change the interface.
46 : f_(f), journal_(f.journal()), state_(SHAMapState::Synching), type_(t)
47{
48 root_ = intr_ptr::make_shared<SHAMapInnerNode>(cowid_);
49}
50
51SHAMap::SHAMap(SHAMap const& other, bool isMutable)
52 : f_(other.f_)
53 , journal_(other.f_.journal())
54 , cowid_(other.cowid_ + 1)
55 , ledgerSeq_(other.ledgerSeq_)
56 , root_(other.root_)
57 , state_(isMutable ? SHAMapState::Modifying : SHAMapState::Immutable)
58 , type_(other.type_)
59 , backed_(other.backed_)
60{
61 // If either map may change, they cannot share nodes
64 {
65 unshare();
66 }
67}
68
70SHAMap::snapShot(bool isMutable) const
71{
72 return std::make_shared<SHAMap>(*this, isMutable);
73}
74
75void
77 SharedPtrNodeStack& stack,
78 uint256 const& target,
80{
81 // walk the tree up from through the inner nodes to the root_
82 // update hashes and links
83 // stack is a path of inner nodes up to, but not including, child
84 // child can be an inner node or a leaf
85
86 XRPL_ASSERT(
88 "xrpl::SHAMap::dirtyUp : valid state");
89 XRPL_ASSERT(
90 child && (child->cowid() == cowid_),
91 "xrpl::SHAMap::dirtyUp : valid child input");
92
93 while (!stack.empty())
94 {
95 auto node =
96 intr_ptr::dynamic_pointer_cast<SHAMapInnerNode>(stack.top().first);
97 SHAMapNodeID nodeID = stack.top().second;
98 stack.pop();
99 XRPL_ASSERT(node, "xrpl::SHAMap::dirtyUp : non-null node");
100
101 int branch = selectBranch(nodeID, target);
102 XRPL_ASSERT(branch >= 0, "xrpl::SHAMap::dirtyUp : valid branch");
103
104 node = unshareNode(std::move(node), nodeID);
105 node->setChild(branch, std::move(child));
106
107 child = std::move(node);
108 }
109}
110
113{
114 XRPL_ASSERT(
115 stack == nullptr || stack->empty(),
116 "xrpl::SHAMap::walkTowardsKey : empty stack input");
117 auto inNode = root_;
118 SHAMapNodeID nodeID;
119
120 while (inNode->isInner())
121 {
122 if (stack != nullptr)
123 stack->push({inNode, nodeID});
124
125 auto const inner =
126 intr_ptr::static_pointer_cast<SHAMapInnerNode>(inNode);
127 auto const branch = selectBranch(nodeID, id);
128 if (inner->isEmptyBranch(branch))
129 return nullptr;
130
131 inNode = descendThrow(*inner, branch);
132 nodeID = nodeID.getChildNodeID(branch);
133 }
134
135 if (stack != nullptr)
136 stack->push({inNode, nodeID});
137 return static_cast<SHAMapLeafNode*>(inNode.get());
138}
139
141SHAMap::findKey(uint256 const& id) const
142{
143 SHAMapLeafNode* leaf = walkTowardsKey(id);
144 if (leaf && leaf->peekItem()->key() != id)
145 leaf = nullptr;
146 return leaf;
147}
148
151{
152 XRPL_ASSERT(backed_, "xrpl::SHAMap::fetchNodeFromDB : is backed");
153 auto obj = f_.db().fetchNodeObject(hash.as_uint256(), ledgerSeq_);
154 return finishFetch(hash, obj);
155}
156
159 SHAMapHash const& hash,
160 std::shared_ptr<NodeObject> const& object) const
161{
162 XRPL_ASSERT(backed_, "xrpl::SHAMap::finishFetch : is backed");
163
164 try
165 {
166 if (!object)
167 {
168 if (full_)
169 {
170 full_ = false;
172 }
173 return {};
174 }
175
176 auto node =
177 SHAMapTreeNode::makeFromPrefix(makeSlice(object->getData()), hash);
178 if (node)
179 canonicalize(hash, node);
180 return node;
181 }
182 catch (std::runtime_error const& e)
183 {
184 JLOG(journal_.warn()) << "finishFetch exception: " << e.what();
185 }
186 catch (...)
187 {
188 JLOG(journal_.warn())
189 << "finishFetch exception: unknown exception: " << hash;
190 }
191
192 return {};
193}
194
195// See if a sync filter has a node
198{
199 if (auto nodeData = filter->getNode(hash))
200 {
201 try
202 {
203 auto node =
205 if (node)
206 {
207 filter->gotNode(
208 true,
209 hash,
211 std::move(*nodeData),
212 node->getType());
213 if (backed_)
214 canonicalize(hash, node);
215 }
216 return node;
217 }
218 catch (std::exception const& x)
219 {
220 JLOG(f_.journal().warn())
221 << "Invalid node/data, hash=" << hash << ": " << x.what();
222 }
223 }
224 return {};
225}
226
227// Get a node without throwing
228// Used on maps where missing nodes are expected
231{
232 auto node = cacheLookup(hash);
233 if (node)
234 return node;
235
236 if (backed_)
237 {
238 node = fetchNodeFromDB(hash);
239 if (node)
240 {
241 canonicalize(hash, node);
242 return node;
243 }
244 }
245
246 if (filter)
247 node = checkFilter(hash, filter);
248
249 return node;
250}
251
254{
255 auto node = cacheLookup(hash);
256
257 if (!node && backed_)
258 node = fetchNodeFromDB(hash);
259
260 return node;
261}
262
263// Throw if the node is missing
266{
267 auto node = fetchNodeNT(hash);
268
269 if (!node)
270 Throw<SHAMapMissingNode>(type_, hash);
271
272 return node;
273}
274
276SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const
277{
278 SHAMapTreeNode* ret = descend(parent, branch);
279
280 if (!ret && !parent->isEmptyBranch(branch))
281 Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
282
283 return ret;
284}
285
287SHAMap::descendThrow(SHAMapInnerNode& parent, int branch) const
288{
289 intr_ptr::SharedPtr<SHAMapTreeNode> ret = descend(parent, branch);
290
291 if (!ret && !parent.isEmptyBranch(branch))
292 Throw<SHAMapMissingNode>(type_, parent.getChildHash(branch));
293
294 return ret;
295}
296
298SHAMap::descend(SHAMapInnerNode* parent, int branch) const
299{
300 SHAMapTreeNode* ret = parent->getChildPointer(branch);
301 if (ret || !backed_)
302 return ret;
303
305 fetchNodeNT(parent->getChildHash(branch));
306 if (!node)
307 return nullptr;
308
309 node = parent->canonicalizeChild(branch, std::move(node));
310 return node.get();
311}
312
314SHAMap::descend(SHAMapInnerNode& parent, int branch) const
315{
316 intr_ptr::SharedPtr<SHAMapTreeNode> node = parent.getChild(branch);
317 if (node || !backed_)
318 return node;
319
320 node = fetchNode(parent.getChildHash(branch));
321 if (!node)
322 return {};
323
324 node = parent.canonicalizeChild(branch, std::move(node));
325 return node;
326}
327
328// Gets the node that would be hooked to this branch,
329// but doesn't hook it up.
331SHAMap::descendNoStore(SHAMapInnerNode& parent, int branch) const
332{
334 if (!ret && backed_)
335 ret = fetchNode(parent.getChildHash(branch));
336 return ret;
337}
338
341 SHAMapInnerNode* parent,
342 SHAMapNodeID const& parentID,
343 int branch,
344 SHAMapSyncFilter* filter) const
345{
346 XRPL_ASSERT(
347 parent->isInner(), "xrpl::SHAMap::descend : valid parent input");
348 XRPL_ASSERT(
349 (branch >= 0) && (branch < branchFactor),
350 "xrpl::SHAMap::descend : valid branch input");
351 XRPL_ASSERT(
352 !parent->isEmptyBranch(branch),
353 "xrpl::SHAMap::descend : parent branch is non-empty");
354
355 SHAMapTreeNode* child = parent->getChildPointer(branch);
356
357 if (!child)
358 {
359 auto const& childHash = parent->getChildHash(branch);
361 fetchNodeNT(childHash, filter);
362
363 if (childNode)
364 {
365 childNode = parent->canonicalizeChild(branch, std::move(childNode));
366 child = childNode.get();
367 }
368 }
369
370 return std::make_pair(child, parentID.getChildNodeID(branch));
371}
372
375 SHAMapInnerNode* parent,
376 int branch,
377 SHAMapSyncFilter* filter,
378 bool& pending,
379 descendCallback&& callback) const
380{
381 pending = false;
382
383 SHAMapTreeNode* ret = parent->getChildPointer(branch);
384 if (ret)
385 return ret;
386
387 auto const& hash = parent->getChildHash(branch);
388
389 auto ptr = cacheLookup(hash);
390 if (!ptr)
391 {
392 if (filter)
393 ptr = checkFilter(hash, filter);
394
395 if (!ptr && backed_)
396 {
397 f_.db().asyncFetch(
398 hash.as_uint256(),
400 [this, hash, cb{std::move(callback)}](
401 std::shared_ptr<NodeObject> const& object) {
402 auto node = finishFetch(hash, object);
403 cb(node, hash);
404 });
405 pending = true;
406 return nullptr;
407 }
408 }
409
410 if (ptr)
411 ptr = parent->canonicalizeChild(branch, std::move(ptr));
412
413 return ptr.get();
414}
415
416template <class Node>
419{
420 // make sure the node is suitable for the intended operation (copy on write)
421 XRPL_ASSERT(
422 node->cowid() <= cowid_,
423 "xrpl::SHAMap::unshareNode : node valid for cowid");
424 if (node->cowid() != cowid_)
425 {
426 // have a CoW
427 XRPL_ASSERT(
429 "xrpl::SHAMap::unshareNode : not immutable");
430 node = intr_ptr::static_pointer_cast<Node>(node->clone(cowid_));
431 if (nodeID.isRoot())
432 root_ = node;
433 }
434 return node;
435}
436
440 SharedPtrNodeStack& stack,
441 int branch,
442 std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const&
443 loopParams) const
444{
445 auto& [init, cmp, incr] = loopParams;
446 if (node->isLeaf())
447 {
448 auto n = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
449 stack.push({node, {leafDepth, n->peekItem()->key()}});
450 return n.get();
451 }
452 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
453 if (stack.empty())
454 stack.push({inner, SHAMapNodeID{}});
455 else
456 stack.push({inner, stack.top().second.getChildNodeID(branch)});
457 for (int i = init; cmp(i);)
458 {
459 if (!inner->isEmptyBranch(i))
460 {
461 node.adopt(descendThrow(inner.get(), i));
462 XRPL_ASSERT(
463 !stack.empty(), "xrpl::SHAMap::belowHelper : non-empty stack");
464 if (node->isLeaf())
465 {
466 auto n = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
467 stack.push({n, {leafDepth, n->peekItem()->key()}});
468 return n.get();
469 }
470 inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
471 stack.push({inner, stack.top().second.getChildNodeID(branch)});
472 i = init; // descend and reset loop
473 }
474 else
475 incr(i); // scan next branch
476 }
477 return nullptr;
478}
482 SharedPtrNodeStack& stack,
483 int branch) const
484{
485 auto init = branchFactor - 1;
486 auto cmp = [](int i) { return i >= 0; };
487 auto incr = [](int& i) { --i; };
488
489 return belowHelper(node, stack, branch, {init, cmp, incr});
490}
494 SharedPtrNodeStack& stack,
495 int branch) const
496{
497 auto init = 0;
498 auto cmp = [](int i) { return i <= branchFactor; };
499 auto incr = [](int& i) { ++i; };
500
501 return belowHelper(node, stack, branch, {init, cmp, incr});
502}
503static boost::intrusive_ptr<SHAMapItem const> const no_item;
504
505boost::intrusive_ptr<SHAMapItem const> const&
507{
508 // If there is only one item below this node, return it
509
510 while (!node->isLeaf())
511 {
512 SHAMapTreeNode* nextNode = nullptr;
513 auto inner = static_cast<SHAMapInnerNode*>(node);
514 for (int i = 0; i < branchFactor; ++i)
515 {
516 if (!inner->isEmptyBranch(i))
517 {
518 if (nextNode)
519 return no_item;
520
521 nextNode = descendThrow(inner, i);
522 }
523 }
524
525 if (!nextNode)
526 {
527 // LCOV_EXCL_START
528 UNREACHABLE("xrpl::SHAMap::onlyBelow : no next node");
529 return no_item;
530 // LCOV_EXCL_STOP
531 }
532
533 node = nextNode;
534 }
535
536 // An inner node must have at least one leaf
537 // below it, unless it's the root_
538 auto const leaf = static_cast<SHAMapLeafNode const*>(node);
539 XRPL_ASSERT(
540 leaf->peekItem() || (leaf == root_.get()),
541 "xrpl::SHAMap::onlyBelow : valid inner node");
542 return leaf->peekItem();
543}
544
545SHAMapLeafNode const*
547{
548 XRPL_ASSERT(
549 stack.empty(), "xrpl::SHAMap::peekFirstItem : empty stack input");
550 SHAMapLeafNode* node = firstBelow(root_, stack);
551 if (!node)
552 {
553 while (!stack.empty())
554 stack.pop();
555 return nullptr;
556 }
557 return node;
558}
559
560SHAMapLeafNode const*
562{
563 XRPL_ASSERT(
564 !stack.empty(), "xrpl::SHAMap::peekNextItem : non-empty stack input");
565 XRPL_ASSERT(
566 stack.top().first->isLeaf(),
567 "xrpl::SHAMap::peekNextItem : stack starts with leaf");
568 stack.pop();
569 while (!stack.empty())
570 {
571 auto [node, nodeID] = stack.top();
572 XRPL_ASSERT(
573 !node->isLeaf(),
574 "xrpl::SHAMap::peekNextItem : another node is not leaf");
575 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
576 for (auto i = selectBranch(nodeID, id) + 1; i < branchFactor; ++i)
577 {
578 if (!inner->isEmptyBranch(i))
579 {
580 node = descendThrow(*inner, i);
581 auto leaf = firstBelow(node, stack, i);
582 if (!leaf)
583 Throw<SHAMapMissingNode>(type_, id);
584 XRPL_ASSERT(
585 leaf->isLeaf(),
586 "xrpl::SHAMap::peekNextItem : leaf is valid");
587 return leaf;
588 }
589 }
590 stack.pop();
591 }
592 // must be last item
593 return nullptr;
594}
595
596boost::intrusive_ptr<SHAMapItem const> const&
597SHAMap::peekItem(uint256 const& id) const
598{
599 SHAMapLeafNode* leaf = findKey(id);
600
601 if (!leaf)
602 return no_item;
603
604 return leaf->peekItem();
605}
606
607boost::intrusive_ptr<SHAMapItem const> const&
608SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const
609{
610 SHAMapLeafNode* leaf = findKey(id);
611
612 if (!leaf)
613 return no_item;
614
615 hash = leaf->getHash();
616 return leaf->peekItem();
617}
618
621{
622 SharedPtrNodeStack stack;
623 walkTowardsKey(id, &stack);
624 while (!stack.empty())
625 {
626 auto [node, nodeID] = stack.top();
627 if (node->isLeaf())
628 {
629 auto leaf = static_cast<SHAMapLeafNode*>(node.get());
630 if (leaf->peekItem()->key() > id)
631 return const_iterator(
632 this, leaf->peekItem().get(), std::move(stack));
633 }
634 else
635 {
636 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
637 for (auto branch = selectBranch(nodeID, id) + 1;
638 branch < branchFactor;
639 ++branch)
640 {
641 if (!inner->isEmptyBranch(branch))
642 {
643 node = descendThrow(*inner, branch);
644 auto leaf = firstBelow(node, stack, branch);
645 if (!leaf)
646 Throw<SHAMapMissingNode>(type_, id);
647 return const_iterator(
648 this, leaf->peekItem().get(), std::move(stack));
649 }
650 }
651 }
652 stack.pop();
653 }
654 return end();
655}
658{
659 SharedPtrNodeStack stack;
660 walkTowardsKey(id, &stack);
661 while (!stack.empty())
662 {
663 auto [node, nodeID] = stack.top();
664 if (node->isLeaf())
665 {
666 auto leaf = static_cast<SHAMapLeafNode*>(node.get());
667 if (leaf->peekItem()->key() < id)
668 return const_iterator(
669 this, leaf->peekItem().get(), std::move(stack));
670 }
671 else
672 {
673 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
674 for (int branch = selectBranch(nodeID, id) - 1; branch >= 0;
675 --branch)
676 {
677 if (!inner->isEmptyBranch(branch))
678 {
679 node = descendThrow(*inner, branch);
680 auto leaf = lastBelow(node, stack, branch);
681 if (!leaf)
682 Throw<SHAMapMissingNode>(type_, id);
683 return const_iterator(
684 this, leaf->peekItem().get(), std::move(stack));
685 }
686 }
687 }
688 stack.pop();
689 }
690 // TODO: what to return here?
691 return end();
692}
693
694bool
695SHAMap::hasItem(uint256 const& id) const
696{
697 return (findKey(id) != nullptr);
698}
699
700bool
702{
703 // delete the item with this ID
704 XRPL_ASSERT(
706 "xrpl::SHAMap::delItem : not immutable");
707
708 SharedPtrNodeStack stack;
709 walkTowardsKey(id, &stack);
710
711 if (stack.empty())
712 Throw<SHAMapMissingNode>(type_, id);
713
714 auto leaf =
715 intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
716 stack.pop();
717
718 if (!leaf || (leaf->peekItem()->key() != id))
719 return false;
720
721 SHAMapNodeType type = leaf->getType();
722
723 // What gets attached to the end of the chain
724 // (For now, nothing, since we deleted the leaf)
726
727 while (!stack.empty())
728 {
729 auto node =
730 intr_ptr::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
731 SHAMapNodeID nodeID = stack.top().second;
732 stack.pop();
733
734 node = unshareNode(std::move(node), nodeID);
735 node->setChild(selectBranch(nodeID, id), std::move(prevNode));
736
737 if (!nodeID.isRoot())
738 {
739 // we may have made this a node with 1 or 0 children
740 // And, if so, we need to remove this branch
741 int const bc = node->getBranchCount();
742 if (bc == 0)
743 {
744 // no children below this branch
745 prevNode.reset();
746 }
747 else if (bc == 1)
748 {
749 // If there's only one item, pull up on the thread
750 auto item = onlyBelow(node.get());
751
752 if (item)
753 {
754 for (int i = 0; i < branchFactor; ++i)
755 {
756 if (!node->isEmptyBranch(i))
757 {
758 node->setChild(
760 break;
761 }
762 }
763
764 prevNode = makeTypedLeaf(type, item, node->cowid());
765 }
766 else
767 {
768 prevNode = std::move(node);
769 }
770 }
771 else
772 {
773 // This node is now the end of the branch
774 prevNode = std::move(node);
775 }
776 }
777 }
778
779 return true;
780}
781
782bool
784 SHAMapNodeType type,
785 boost::intrusive_ptr<SHAMapItem const> item)
786{
787 XRPL_ASSERT(
789 "xrpl::SHAMap::addGiveItem : not immutable");
790 XRPL_ASSERT(
792 "xrpl::SHAMap::addGiveItem : valid type input");
793
794 // add the specified item, does not update
795 uint256 tag = item->key();
796
797 SharedPtrNodeStack stack;
798 walkTowardsKey(tag, &stack);
799
800 if (stack.empty())
801 Throw<SHAMapMissingNode>(type_, tag);
802
803 auto [node, nodeID] = stack.top();
804 stack.pop();
805
806 if (node->isLeaf())
807 {
808 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
809 if (leaf->peekItem()->key() == tag)
810 return false;
811 }
812 node = unshareNode(std::move(node), nodeID);
813 if (node->isInner())
814 {
815 // easy case, we end on an inner node
816 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
817 int branch = selectBranch(nodeID, tag);
818 XRPL_ASSERT(
819 inner->isEmptyBranch(branch),
820 "xrpl::SHAMap::addGiveItem : inner branch is empty");
821 inner->setChild(branch, makeTypedLeaf(type, std::move(item), cowid_));
822 }
823 else
824 {
825 // this is a leaf node that has to be made an inner node holding two
826 // items
827 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
828 auto otherItem = leaf->peekItem();
829 XRPL_ASSERT(
830 otherItem && (tag != otherItem->key()),
831 "xrpl::SHAMap::addGiveItem : non-null item");
832
833 node = intr_ptr::make_shared<SHAMapInnerNode>(node->cowid());
834
835 unsigned int b1, b2;
836
837 while ((b1 = selectBranch(nodeID, tag)) ==
838 (b2 = selectBranch(nodeID, otherItem->key())))
839 {
840 stack.push({node, nodeID});
841
842 // we need a new inner node, since both go on same branch at this
843 // level
844 nodeID = nodeID.getChildNodeID(b1);
845 node = intr_ptr::make_shared<SHAMapInnerNode>(cowid_);
846 }
847
848 // we can add the two leaf nodes here
849 XRPL_ASSERT(
850 node->isInner(), "xrpl::SHAMap::addGiveItem : node is inner");
851
852 auto inner = static_cast<SHAMapInnerNode*>(node.get());
853 inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
854 inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
855 }
856
857 dirtyUp(stack, tag, node);
858 return true;
859}
860
861bool
863 SHAMapNodeType type,
864 boost::intrusive_ptr<SHAMapItem const> item)
865{
866 return addGiveItem(type, std::move(item));
867}
868
871{
872 auto hash = root_->getHash();
873 if (hash.isZero())
874 {
875 const_cast<SHAMap&>(*this).unshare();
876 hash = root_->getHash();
877 }
878 return hash;
879}
880
881bool
883 SHAMapNodeType type,
884 boost::intrusive_ptr<SHAMapItem const> item)
885{
886 // can't change the tag but can change the hash
887 uint256 tag = item->key();
888
889 XRPL_ASSERT(
891 "xrpl::SHAMap::updateGiveItem : not immutable");
892
893 SharedPtrNodeStack stack;
894 walkTowardsKey(tag, &stack);
895
896 if (stack.empty())
897 Throw<SHAMapMissingNode>(type_, tag);
898
899 auto node =
900 intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
901 auto nodeID = stack.top().second;
902 stack.pop();
903
904 if (!node || (node->peekItem()->key() != tag))
905 {
906 // LCOV_EXCL_START
907 UNREACHABLE("xrpl::SHAMap::updateGiveItem : invalid node");
908 return false;
909 // LCOV_EXCL_STOP
910 }
911
912 if (node->getType() != type)
913 {
914 JLOG(journal_.fatal()) << "SHAMap::updateGiveItem: cross-type change!";
915 return false;
916 }
917
918 node = unshareNode(std::move(node), nodeID);
919
920 if (node->setItem(item))
921 dirtyUp(stack, tag, node);
922
923 return true;
924}
925
926bool
928{
929 if (hash == root_->getHash())
930 return true;
931
932 if (auto stream = journal_.trace())
933 {
935 {
936 stream << "Fetch root TXN node " << hash;
937 }
938 else if (type_ == SHAMapType::STATE)
939 {
940 stream << "Fetch root STATE node " << hash;
941 }
942 else
943 {
944 stream << "Fetch root SHAMap node " << hash;
945 }
946 }
947
948 auto newRoot = fetchNodeNT(hash, filter);
949
950 if (newRoot)
951 {
952 root_ = newRoot;
953 XRPL_ASSERT(
954 root_->getHash() == hash,
955 "xrpl::SHAMap::fetchRoot : root hash do match");
956 return true;
957 }
958
959 return false;
960}
961
976 const
977{
978 XRPL_ASSERT(
979 node->cowid() == 0, "xrpl::SHAMap::writeNode : valid input node");
980 XRPL_ASSERT(backed_, "xrpl::SHAMap::writeNode : is backed");
981
982 canonicalize(node->getHash(), node);
983
984 Serializer s;
985 node->serializeWithPrefix(s);
986 f_.db().store(
987 t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_);
988 return node;
989}
990
991// We can't modify an inner node someone else might have a
992// pointer to because flushing modifies inner nodes -- it
993// makes them point to canonical/shared nodes.
994template <class Node>
997{
998 // A shared node should never need to be flushed
999 // because that would imply someone modified it
1000 XRPL_ASSERT(node->cowid(), "xrpl::SHAMap::preFlushNode : valid input node");
1001
1002 if (node->cowid() != cowid_)
1003 {
1004 // Node is not uniquely ours, so unshare it before
1005 // possibly modifying it
1006 node = intr_ptr::static_pointer_cast<Node>(node->clone(cowid_));
1007 }
1008 return node;
1009}
1010
1011int
1013{
1014 // Don't share nodes with parent map
1015 return walkSubTree(false, hotUNKNOWN);
1016}
1017
1018int
1020{
1021 // We only write back if this map is backed.
1022 return walkSubTree(backed_, t);
1023}
1024
1025int
1027{
1028 XRPL_ASSERT(!doWrite || backed_, "xrpl::SHAMap::walkSubTree : valid input");
1029
1030 int flushed = 0;
1031
1032 if (!root_ || (root_->cowid() == 0))
1033 return flushed;
1034
1035 if (root_->isLeaf())
1036 { // special case -- root_ is leaf
1037 root_ = preFlushNode(std::move(root_));
1038 root_->updateHash();
1039 root_->unshare();
1040
1041 if (doWrite)
1042 root_ = writeNode(t, std::move(root_));
1043
1044 return 1;
1045 }
1046
1047 auto node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(root_);
1048
1049 if (node->isEmpty())
1050 { // replace empty root with a new empty root
1051 root_ = intr_ptr::make_shared<SHAMapInnerNode>(0);
1052 return 1;
1053 }
1054
1055 // Stack of {parent,index,child} pointers representing
1056 // inner nodes we are in the process of flushing
1057 using StackEntry = std::pair<intr_ptr::SharedPtr<SHAMapInnerNode>, int>;
1059
1060 node = preFlushNode(std::move(node));
1061
1062 int pos = 0;
1063
1064 // We can't flush an inner node until we flush its children
1065 while (1)
1066 {
1067 while (pos < branchFactor)
1068 {
1069 if (node->isEmptyBranch(pos))
1070 {
1071 ++pos;
1072 }
1073 else
1074 {
1075 // No need to do I/O. If the node isn't linked,
1076 // it can't need to be flushed
1077 int branch = pos;
1078 auto child = node->getChild(pos++);
1079
1080 if (child && (child->cowid() != 0))
1081 {
1082 // This is a node that needs to be flushed
1083
1084 child = preFlushNode(std::move(child));
1085
1086 if (child->isInner())
1087 {
1088 // save our place and work on this node
1089
1090 stack.emplace(std::move(node), branch);
1091 // The semantics of this changes when we move to c++-20
1092 // Right now no move will occur; With c++-20 child will
1093 // be moved from.
1094 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
1095 std::move(child));
1096 pos = 0;
1097 }
1098 else
1099 {
1100 // flush this leaf
1101 ++flushed;
1102
1103 XRPL_ASSERT(
1104 node->cowid() == cowid_,
1105 "xrpl::SHAMap::walkSubTree : node cowid do "
1106 "match");
1107 child->updateHash();
1108 child->unshare();
1109
1110 if (doWrite)
1111 child = writeNode(t, std::move(child));
1112
1113 node->shareChild(branch, child);
1114 }
1115 }
1116 }
1117 }
1118
1119 // update the hash of this inner node
1120 node->updateHashDeep();
1121
1122 // This inner node can now be shared
1123 node->unshare();
1124
1125 if (doWrite)
1126 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
1127 writeNode(t, std::move(node)));
1128
1129 ++flushed;
1130
1131 if (stack.empty())
1132 break;
1133
1134 auto parent = std::move(stack.top().first);
1135 pos = stack.top().second;
1136 stack.pop();
1137
1138 // Hook this inner node to its parent
1139 XRPL_ASSERT(
1140 parent->cowid() == cowid_,
1141 "xrpl::SHAMap::walkSubTree : parent cowid do match");
1142 parent->shareChild(pos, node);
1143
1144 // Continue with parent's next child, if any
1145 node = std::move(parent);
1146 ++pos;
1147 }
1148
1149 // Last inner node is the new root_
1150 root_ = std::move(node);
1151
1152 return flushed;
1153}
1154
1155void
1156SHAMap::dump(bool hash) const
1157{
1158 int leafCount = 0;
1159 JLOG(journal_.info()) << " MAP Contains";
1160
1162 stack.push({root_.get(), SHAMapNodeID()});
1163
1164 do
1165 {
1166 auto [node, nodeID] = stack.top();
1167 stack.pop();
1168
1169 JLOG(journal_.info()) << node->getString(nodeID);
1170 if (hash)
1171 {
1172 JLOG(journal_.info()) << "Hash: " << node->getHash();
1173 }
1174
1175 if (node->isInner())
1176 {
1177 auto inner = static_cast<SHAMapInnerNode*>(node);
1178 for (int i = 0; i < branchFactor; ++i)
1179 {
1180 if (!inner->isEmptyBranch(i))
1181 {
1182 auto child = inner->getChildPointer(i);
1183 if (child)
1184 {
1185 XRPL_ASSERT(
1186 child->getHash() == inner->getChildHash(i),
1187 "xrpl::SHAMap::dump : child hash do match");
1188 stack.push({child, nodeID.getChildNodeID(i)});
1189 }
1190 }
1191 }
1192 }
1193 else
1194 ++leafCount;
1195 } while (!stack.empty());
1196
1197 JLOG(journal_.info()) << leafCount << " resident leaves";
1198}
1199
1202{
1203 auto ret = f_.getTreeNodeCache()->fetch(hash.as_uint256());
1204 XRPL_ASSERT(
1205 !ret || !ret->cowid(),
1206 "xrpl::SHAMap::cacheLookup : not found or zero cowid");
1207 return ret;
1208}
1209
1210void
1212 SHAMapHash const& hash,
1214{
1215 XRPL_ASSERT(backed_, "xrpl::SHAMap::canonicalize : is backed");
1216 XRPL_ASSERT(
1217 node->cowid() == 0, "xrpl::SHAMap::canonicalize : valid node input");
1218 XRPL_ASSERT(
1219 node->getHash() == hash,
1220 "xrpl::SHAMap::canonicalize : node hash do match");
1221
1222 f_.getTreeNodeCache()->canonicalize_replace_client(hash.as_uint256(), node);
1223}
1224
1225void
1227{
1228 (void)getHash(); // update node hashes
1229 auto node = root_.get();
1230 XRPL_ASSERT(node, "xrpl::SHAMap::invariants : non-null root node");
1231 XRPL_ASSERT(
1232 !node->isLeaf(), "xrpl::SHAMap::invariants : root node is not leaf");
1233 SharedPtrNodeStack stack;
1234 for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1235 leaf = peekNextItem(leaf->peekItem()->key(), stack))
1236 ;
1237 node->invariants(true);
1238}
1239
1240} // namespace xrpl
Stream fatal() const
Definition Journal.h:333
Stream info() const
Definition Journal.h:315
Stream trace() const
Severity stream access functions.
Definition Journal.h:303
Stream warn() const
Definition Journal.h:321
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.
Definition Database.cpp:165
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous, bool duplicate=false)
Fetch a node object.
Definition Database.cpp:221
virtual void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t ledgerSeq)=0
Store the object.
uint256 const & as_uint256() const
Definition SHAMapHash.h:25
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)
void setChild(int m, intr_ptr::SharedPtr< SHAMapTreeNode > child)
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
bool isRoot() 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.
Definition SHAMap.h:78
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:298
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
Definition SHAMap.cpp:112
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:862
const_iterator upper_bound(uint256 const &id) const
Find the first item after the given item.
Definition SHAMap.cpp:620
SHAMapState state_
Definition SHAMap.h:90
const_iterator lower_bound(uint256 const &id) const
Find the object with the greatest object id smaller than the input id.
Definition SHAMap.cpp:657
bool full_
Definition SHAMap.h:93
intr_ptr::SharedPtr< SHAMapTreeNode > checkFilter(SHAMapHash const &hash, SHAMapSyncFilter *filter) const
Definition SHAMap.cpp:197
const_iterator end() const
Definition SHAMap.h:741
SHAMapLeafNode * firstBelow(intr_ptr::SharedPtr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) const
Definition SHAMap.cpp:492
intr_ptr::SharedPtr< SHAMapTreeNode > writeNode(NodeObjectType t, intr_ptr::SharedPtr< SHAMapTreeNode > node) const
write and canonicalize modified node
Definition SHAMap.cpp:975
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
Definition SHAMap.cpp:374
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
Definition SHAMap.h:84
Family & f_
Definition SHAMap.h:80
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition SHAMap.cpp:597
int flushDirty(NodeObjectType t)
Flush modified nodes to the nodestore and convert them to shared.
Definition SHAMap.cpp:1019
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeFromDB(SHAMapHash const &hash) const
Definition SHAMap.cpp:150
SHAMapLeafNode * lastBelow(intr_ptr::SharedPtr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch=branchFactor) const
Definition SHAMap.cpp:480
static constexpr unsigned int leafDepth
The depth of the hash map: data is only present in the leaves.
Definition SHAMap.h:102
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNode(SHAMapHash const &hash) const
Definition SHAMap.cpp:265
SHAMapLeafNode const * peekNextItem(uint256 const &id, SharedPtrNodeStack &stack) const
Definition SHAMap.cpp:561
void dump(bool withHashes=false) const
Definition SHAMap.cpp:1156
beast::Journal journal_
Definition SHAMap.h:81
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
Definition SHAMap.cpp:438
bool backed_
Definition SHAMap.h:92
static constexpr unsigned int branchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map)
Definition SHAMap.h:98
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition SHAMap.cpp:70
bool hasItem(uint256 const &id) const
Does the tree have an item with the given ID?
Definition SHAMap.cpp:695
int unshare()
Convert any modified nodes to shared.
Definition SHAMap.cpp:1012
bool updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:882
SHAMapType const type_
Definition SHAMap.h:91
SHAMap()=delete
intr_ptr::SharedPtr< SHAMapTreeNode > finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
Definition SHAMap.cpp:158
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeNT(SHAMapHash const &hash) const
Definition SHAMap.cpp:253
void canonicalize(SHAMapHash const &hash, intr_ptr::SharedPtr< SHAMapTreeNode > &) const
Definition SHAMap.cpp:1211
void invariants() const
Definition SHAMap.cpp:1226
SHAMapLeafNode * findKey(uint256 const &id) const
Return nullptr if key not found.
Definition SHAMap.cpp:141
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:783
intr_ptr::SharedPtr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
Definition SHAMap.cpp:1201
intr_ptr::SharedPtr< Node > preFlushNode(intr_ptr::SharedPtr< Node > node) const
prepare a node to be modified before flushing
Definition SHAMap.cpp:996
intr_ptr::SharedPtr< Node > unshareNode(intr_ptr::SharedPtr< Node >, SHAMapNodeID const &nodeID)
Unshare the node, allowing it to be modified.
Definition SHAMap.cpp:418
boost::intrusive_ptr< SHAMapItem const > const & onlyBelow(SHAMapTreeNode *) const
If there is only one leaf below this node, get its contents.
Definition SHAMap.cpp:506
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, intr_ptr::SharedPtr< SHAMapTreeNode > terminal)
Update hashes up to the root.
Definition SHAMap.cpp:76
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
Definition SHAMap.cpp:927
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
Definition SHAMap.h:87
intr_ptr::SharedPtr< SHAMapTreeNode > descendNoStore(SHAMapInnerNode &, int branch) const
Definition SHAMap.cpp:331
int walkSubTree(bool doWrite, NodeObjectType t)
Definition SHAMap.cpp:1026
SHAMapHash getHash() const
Definition SHAMap.cpp:870
bool delItem(uint256 const &id)
Definition SHAMap.cpp:701
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:276
intr_ptr::SharedPtr< SHAMapTreeNode > root_
Definition SHAMap.h:89
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
Definition SHAMap.cpp:546
A shared intrusive pointer class that supports weak pointers.
T * get() const
Get the raw pointer.
void reset()
Set the pointer to null, decrement the strong count, and run the appropriate release action.
void adopt(T *p)
Adopt the raw pointer.
T emplace(T... args)
T empty(T... args)
T is_same_v
T make_pair(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
static boost::intrusive_ptr< SHAMapItem const > const no_item
Definition SHAMap.cpp:503
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.
Definition NodeObject.h:13
@ hotUNKNOWN
Definition NodeObject.h:14
intr_ptr::SharedPtr< SHAMapLeafNode > makeTypedLeaf(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item, std::uint32_t owner)
Definition SHAMap.cpp:13
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.
Definition SHAMap.h:28
@ 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)
Definition Slice.h:225
T pop(T... args)
T push(T... args)
T to_string(T... args)
T top(T... args)
T what(T... args)