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 ripple {
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 "ripple::SHAMap::dirtyUp : valid state");
89 XRPL_ASSERT(
90 child && (child->cowid() == cowid_),
91 "ripple::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, "ripple::SHAMap::dirtyUp : non-null node");
100
101 int branch = selectBranch(nodeID, target);
102 XRPL_ASSERT(branch >= 0, "ripple::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 "ripple::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_, "ripple::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_, "ripple::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: unknonw 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(), "ripple::SHAMap::descend : valid parent input");
348 XRPL_ASSERT(
349 (branch >= 0) && (branch < branchFactor),
350 "ripple::SHAMap::descend : valid branch input");
351 XRPL_ASSERT(
352 !parent->isEmptyBranch(branch),
353 "ripple::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 "ripple::SHAMap::unshareNode : node valid for cowid");
424 if (node->cowid() != cowid_)
425 {
426 // have a CoW
427 XRPL_ASSERT(
429 "ripple::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(),
464 "ripple::SHAMap::belowHelper : non-empty stack");
465 if (node->isLeaf())
466 {
467 auto n = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
468 stack.push({n, {leafDepth, n->peekItem()->key()}});
469 return n.get();
470 }
471 inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
472 stack.push({inner, stack.top().second.getChildNodeID(branch)});
473 i = init; // descend and reset loop
474 }
475 else
476 incr(i); // scan next branch
477 }
478 return nullptr;
479}
483 SharedPtrNodeStack& stack,
484 int branch) const
485{
486 auto init = branchFactor - 1;
487 auto cmp = [](int i) { return i >= 0; };
488 auto incr = [](int& i) { --i; };
489
490 return belowHelper(node, stack, branch, {init, cmp, incr});
491}
495 SharedPtrNodeStack& stack,
496 int branch) const
497{
498 auto init = 0;
499 auto cmp = [](int i) { return i <= branchFactor; };
500 auto incr = [](int& i) { ++i; };
501
502 return belowHelper(node, stack, branch, {init, cmp, incr});
503}
504static boost::intrusive_ptr<SHAMapItem const> const no_item;
505
506boost::intrusive_ptr<SHAMapItem const> const&
508{
509 // If there is only one item below this node, return it
510
511 while (!node->isLeaf())
512 {
513 SHAMapTreeNode* nextNode = nullptr;
514 auto inner = static_cast<SHAMapInnerNode*>(node);
515 for (int i = 0; i < branchFactor; ++i)
516 {
517 if (!inner->isEmptyBranch(i))
518 {
519 if (nextNode)
520 return no_item;
521
522 nextNode = descendThrow(inner, i);
523 }
524 }
525
526 if (!nextNode)
527 {
528 // LCOV_EXCL_START
529 UNREACHABLE("ripple::SHAMap::onlyBelow : no next node");
530 return no_item;
531 // LCOV_EXCL_STOP
532 }
533
534 node = nextNode;
535 }
536
537 // An inner node must have at least one leaf
538 // below it, unless it's the root_
539 auto const leaf = static_cast<SHAMapLeafNode const*>(node);
540 XRPL_ASSERT(
541 leaf->peekItem() || (leaf == root_.get()),
542 "ripple::SHAMap::onlyBelow : valid inner node");
543 return leaf->peekItem();
544}
545
546SHAMapLeafNode const*
548{
549 XRPL_ASSERT(
550 stack.empty(), "ripple::SHAMap::peekFirstItem : empty stack input");
551 SHAMapLeafNode* node = firstBelow(root_, stack);
552 if (!node)
553 {
554 while (!stack.empty())
555 stack.pop();
556 return nullptr;
557 }
558 return node;
559}
560
561SHAMapLeafNode const*
563{
564 XRPL_ASSERT(
565 !stack.empty(), "ripple::SHAMap::peekNextItem : non-empty stack input");
566 XRPL_ASSERT(
567 stack.top().first->isLeaf(),
568 "ripple::SHAMap::peekNextItem : stack starts with leaf");
569 stack.pop();
570 while (!stack.empty())
571 {
572 auto [node, nodeID] = stack.top();
573 XRPL_ASSERT(
574 !node->isLeaf(),
575 "ripple::SHAMap::peekNextItem : another node is not leaf");
576 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
577 for (auto i = selectBranch(nodeID, id) + 1; i < branchFactor; ++i)
578 {
579 if (!inner->isEmptyBranch(i))
580 {
581 node = descendThrow(*inner, i);
582 auto leaf = firstBelow(node, stack, i);
583 if (!leaf)
584 Throw<SHAMapMissingNode>(type_, id);
585 XRPL_ASSERT(
586 leaf->isLeaf(),
587 "ripple::SHAMap::peekNextItem : leaf is valid");
588 return leaf;
589 }
590 }
591 stack.pop();
592 }
593 // must be last item
594 return nullptr;
595}
596
597boost::intrusive_ptr<SHAMapItem const> const&
598SHAMap::peekItem(uint256 const& id) const
599{
600 SHAMapLeafNode* leaf = findKey(id);
601
602 if (!leaf)
603 return no_item;
604
605 return leaf->peekItem();
606}
607
608boost::intrusive_ptr<SHAMapItem const> const&
609SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const
610{
611 SHAMapLeafNode* leaf = findKey(id);
612
613 if (!leaf)
614 return no_item;
615
616 hash = leaf->getHash();
617 return leaf->peekItem();
618}
619
622{
623 SharedPtrNodeStack stack;
624 walkTowardsKey(id, &stack);
625 while (!stack.empty())
626 {
627 auto [node, nodeID] = stack.top();
628 if (node->isLeaf())
629 {
630 auto leaf = static_cast<SHAMapLeafNode*>(node.get());
631 if (leaf->peekItem()->key() > id)
632 return const_iterator(
633 this, leaf->peekItem().get(), std::move(stack));
634 }
635 else
636 {
637 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
638 for (auto branch = selectBranch(nodeID, id) + 1;
639 branch < branchFactor;
640 ++branch)
641 {
642 if (!inner->isEmptyBranch(branch))
643 {
644 node = descendThrow(*inner, branch);
645 auto leaf = firstBelow(node, stack, branch);
646 if (!leaf)
647 Throw<SHAMapMissingNode>(type_, id);
648 return const_iterator(
649 this, leaf->peekItem().get(), std::move(stack));
650 }
651 }
652 }
653 stack.pop();
654 }
655 return end();
656}
659{
660 SharedPtrNodeStack stack;
661 walkTowardsKey(id, &stack);
662 while (!stack.empty())
663 {
664 auto [node, nodeID] = stack.top();
665 if (node->isLeaf())
666 {
667 auto leaf = static_cast<SHAMapLeafNode*>(node.get());
668 if (leaf->peekItem()->key() < id)
669 return const_iterator(
670 this, leaf->peekItem().get(), std::move(stack));
671 }
672 else
673 {
674 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
675 for (int branch = selectBranch(nodeID, id) - 1; branch >= 0;
676 --branch)
677 {
678 if (!inner->isEmptyBranch(branch))
679 {
680 node = descendThrow(*inner, branch);
681 auto leaf = lastBelow(node, stack, branch);
682 if (!leaf)
683 Throw<SHAMapMissingNode>(type_, id);
684 return const_iterator(
685 this, leaf->peekItem().get(), std::move(stack));
686 }
687 }
688 }
689 stack.pop();
690 }
691 // TODO: what to return here?
692 return end();
693}
694
695bool
696SHAMap::hasItem(uint256 const& id) const
697{
698 return (findKey(id) != nullptr);
699}
700
701bool
703{
704 // delete the item with this ID
705 XRPL_ASSERT(
707 "ripple::SHAMap::delItem : not immutable");
708
709 SharedPtrNodeStack stack;
710 walkTowardsKey(id, &stack);
711
712 if (stack.empty())
713 Throw<SHAMapMissingNode>(type_, id);
714
715 auto leaf =
716 intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
717 stack.pop();
718
719 if (!leaf || (leaf->peekItem()->key() != id))
720 return false;
721
722 SHAMapNodeType type = leaf->getType();
723
724 // What gets attached to the end of the chain
725 // (For now, nothing, since we deleted the leaf)
727
728 while (!stack.empty())
729 {
730 auto node =
731 intr_ptr::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
732 SHAMapNodeID nodeID = stack.top().second;
733 stack.pop();
734
735 node = unshareNode(std::move(node), nodeID);
736 node->setChild(selectBranch(nodeID, id), std::move(prevNode));
737
738 if (!nodeID.isRoot())
739 {
740 // we may have made this a node with 1 or 0 children
741 // And, if so, we need to remove this branch
742 int const bc = node->getBranchCount();
743 if (bc == 0)
744 {
745 // no children below this branch
746 prevNode.reset();
747 }
748 else if (bc == 1)
749 {
750 // If there's only one item, pull up on the thread
751 auto item = onlyBelow(node.get());
752
753 if (item)
754 {
755 for (int i = 0; i < branchFactor; ++i)
756 {
757 if (!node->isEmptyBranch(i))
758 {
759 node->setChild(
761 break;
762 }
763 }
764
765 prevNode = makeTypedLeaf(type, item, node->cowid());
766 }
767 else
768 {
769 prevNode = std::move(node);
770 }
771 }
772 else
773 {
774 // This node is now the end of the branch
775 prevNode = std::move(node);
776 }
777 }
778 }
779
780 return true;
781}
782
783bool
785 SHAMapNodeType type,
786 boost::intrusive_ptr<SHAMapItem const> item)
787{
788 XRPL_ASSERT(
790 "ripple::SHAMap::addGiveItem : not immutable");
791 XRPL_ASSERT(
793 "ripple::SHAMap::addGiveItem : valid type input");
794
795 // add the specified item, does not update
796 uint256 tag = item->key();
797
798 SharedPtrNodeStack stack;
799 walkTowardsKey(tag, &stack);
800
801 if (stack.empty())
802 Throw<SHAMapMissingNode>(type_, tag);
803
804 auto [node, nodeID] = stack.top();
805 stack.pop();
806
807 if (node->isLeaf())
808 {
809 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
810 if (leaf->peekItem()->key() == tag)
811 return false;
812 }
813 node = unshareNode(std::move(node), nodeID);
814 if (node->isInner())
815 {
816 // easy case, we end on an inner node
817 auto inner = intr_ptr::static_pointer_cast<SHAMapInnerNode>(node);
818 int branch = selectBranch(nodeID, tag);
819 XRPL_ASSERT(
820 inner->isEmptyBranch(branch),
821 "ripple::SHAMap::addGiveItem : inner branch is empty");
822 inner->setChild(branch, makeTypedLeaf(type, std::move(item), cowid_));
823 }
824 else
825 {
826 // this is a leaf node that has to be made an inner node holding two
827 // items
828 auto leaf = intr_ptr::static_pointer_cast<SHAMapLeafNode>(node);
829 auto otherItem = leaf->peekItem();
830 XRPL_ASSERT(
831 otherItem && (tag != otherItem->key()),
832 "ripple::SHAMap::addGiveItem : non-null item");
833
834 node = intr_ptr::make_shared<SHAMapInnerNode>(node->cowid());
835
836 unsigned int b1, b2;
837
838 while ((b1 = selectBranch(nodeID, tag)) ==
839 (b2 = selectBranch(nodeID, otherItem->key())))
840 {
841 stack.push({node, nodeID});
842
843 // we need a new inner node, since both go on same branch at this
844 // level
845 nodeID = nodeID.getChildNodeID(b1);
846 node = intr_ptr::make_shared<SHAMapInnerNode>(cowid_);
847 }
848
849 // we can add the two leaf nodes here
850 XRPL_ASSERT(
851 node->isInner(), "ripple::SHAMap::addGiveItem : node is inner");
852
853 auto inner = static_cast<SHAMapInnerNode*>(node.get());
854 inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
855 inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
856 }
857
858 dirtyUp(stack, tag, node);
859 return true;
860}
861
862bool
864 SHAMapNodeType type,
865 boost::intrusive_ptr<SHAMapItem const> item)
866{
867 return addGiveItem(type, std::move(item));
868}
869
872{
873 auto hash = root_->getHash();
874 if (hash.isZero())
875 {
876 const_cast<SHAMap&>(*this).unshare();
877 hash = root_->getHash();
878 }
879 return hash;
880}
881
882bool
884 SHAMapNodeType type,
885 boost::intrusive_ptr<SHAMapItem const> item)
886{
887 // can't change the tag but can change the hash
888 uint256 tag = item->key();
889
890 XRPL_ASSERT(
892 "ripple::SHAMap::updateGiveItem : not immutable");
893
894 SharedPtrNodeStack stack;
895 walkTowardsKey(tag, &stack);
896
897 if (stack.empty())
898 Throw<SHAMapMissingNode>(type_, tag);
899
900 auto node =
901 intr_ptr::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
902 auto nodeID = stack.top().second;
903 stack.pop();
904
905 if (!node || (node->peekItem()->key() != tag))
906 {
907 // LCOV_EXCL_START
908 UNREACHABLE("ripple::SHAMap::updateGiveItem : invalid node");
909 return false;
910 // LCOV_EXCL_STOP
911 }
912
913 if (node->getType() != type)
914 {
915 JLOG(journal_.fatal()) << "SHAMap::updateGiveItem: cross-type change!";
916 return false;
917 }
918
919 node = unshareNode(std::move(node), nodeID);
920
921 if (node->setItem(item))
922 dirtyUp(stack, tag, node);
923
924 return true;
925}
926
927bool
929{
930 if (hash == root_->getHash())
931 return true;
932
933 if (auto stream = journal_.trace())
934 {
936 {
937 stream << "Fetch root TXN node " << hash;
938 }
939 else if (type_ == SHAMapType::STATE)
940 {
941 stream << "Fetch root STATE node " << hash;
942 }
943 else
944 {
945 stream << "Fetch root SHAMap node " << hash;
946 }
947 }
948
949 auto newRoot = fetchNodeNT(hash, filter);
950
951 if (newRoot)
952 {
953 root_ = newRoot;
954 XRPL_ASSERT(
955 root_->getHash() == hash,
956 "ripple::SHAMap::fetchRoot : root hash do match");
957 return true;
958 }
959
960 return false;
961}
962
977 const
978{
979 XRPL_ASSERT(
980 node->cowid() == 0, "ripple::SHAMap::writeNode : valid input node");
981 XRPL_ASSERT(backed_, "ripple::SHAMap::writeNode : is backed");
982
983 canonicalize(node->getHash(), node);
984
985 Serializer s;
986 node->serializeWithPrefix(s);
987 f_.db().store(
988 t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_);
989 return node;
990}
991
992// We can't modify an inner node someone else might have a
993// pointer to because flushing modifies inner nodes -- it
994// makes them point to canonical/shared nodes.
995template <class Node>
998{
999 // A shared node should never need to be flushed
1000 // because that would imply someone modified it
1001 XRPL_ASSERT(
1002 node->cowid(), "ripple::SHAMap::preFlushNode : valid input node");
1003
1004 if (node->cowid() != cowid_)
1005 {
1006 // Node is not uniquely ours, so unshare it before
1007 // possibly modifying it
1008 node = intr_ptr::static_pointer_cast<Node>(node->clone(cowid_));
1009 }
1010 return node;
1011}
1012
1013int
1015{
1016 // Don't share nodes with parent map
1017 return walkSubTree(false, hotUNKNOWN);
1018}
1019
1020int
1022{
1023 // We only write back if this map is backed.
1024 return walkSubTree(backed_, t);
1025}
1026
1027int
1029{
1030 XRPL_ASSERT(
1031 !doWrite || backed_, "ripple::SHAMap::walkSubTree : valid input");
1032
1033 int flushed = 0;
1034
1035 if (!root_ || (root_->cowid() == 0))
1036 return flushed;
1037
1038 if (root_->isLeaf())
1039 { // special case -- root_ is leaf
1040 root_ = preFlushNode(std::move(root_));
1041 root_->updateHash();
1042 root_->unshare();
1043
1044 if (doWrite)
1045 root_ = writeNode(t, std::move(root_));
1046
1047 return 1;
1048 }
1049
1050 auto node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(root_);
1051
1052 if (node->isEmpty())
1053 { // replace empty root with a new empty root
1054 root_ = intr_ptr::make_shared<SHAMapInnerNode>(0);
1055 return 1;
1056 }
1057
1058 // Stack of {parent,index,child} pointers representing
1059 // inner nodes we are in the process of flushing
1060 using StackEntry = std::pair<intr_ptr::SharedPtr<SHAMapInnerNode>, int>;
1062
1063 node = preFlushNode(std::move(node));
1064
1065 int pos = 0;
1066
1067 // We can't flush an inner node until we flush its children
1068 while (1)
1069 {
1070 while (pos < branchFactor)
1071 {
1072 if (node->isEmptyBranch(pos))
1073 {
1074 ++pos;
1075 }
1076 else
1077 {
1078 // No need to do I/O. If the node isn't linked,
1079 // it can't need to be flushed
1080 int branch = pos;
1081 auto child = node->getChild(pos++);
1082
1083 if (child && (child->cowid() != 0))
1084 {
1085 // This is a node that needs to be flushed
1086
1087 child = preFlushNode(std::move(child));
1088
1089 if (child->isInner())
1090 {
1091 // save our place and work on this node
1092
1093 stack.emplace(std::move(node), branch);
1094 // The semantics of this changes when we move to c++-20
1095 // Right now no move will occur; With c++-20 child will
1096 // be moved from.
1097 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
1098 std::move(child));
1099 pos = 0;
1100 }
1101 else
1102 {
1103 // flush this leaf
1104 ++flushed;
1105
1106 XRPL_ASSERT(
1107 node->cowid() == cowid_,
1108 "ripple::SHAMap::walkSubTree : node cowid do "
1109 "match");
1110 child->updateHash();
1111 child->unshare();
1112
1113 if (doWrite)
1114 child = writeNode(t, std::move(child));
1115
1116 node->shareChild(branch, child);
1117 }
1118 }
1119 }
1120 }
1121
1122 // update the hash of this inner node
1123 node->updateHashDeep();
1124
1125 // This inner node can now be shared
1126 node->unshare();
1127
1128 if (doWrite)
1129 node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(
1130 writeNode(t, std::move(node)));
1131
1132 ++flushed;
1133
1134 if (stack.empty())
1135 break;
1136
1137 auto parent = std::move(stack.top().first);
1138 pos = stack.top().second;
1139 stack.pop();
1140
1141 // Hook this inner node to its parent
1142 XRPL_ASSERT(
1143 parent->cowid() == cowid_,
1144 "ripple::SHAMap::walkSubTree : parent cowid do match");
1145 parent->shareChild(pos, node);
1146
1147 // Continue with parent's next child, if any
1148 node = std::move(parent);
1149 ++pos;
1150 }
1151
1152 // Last inner node is the new root_
1153 root_ = std::move(node);
1154
1155 return flushed;
1156}
1157
1158void
1159SHAMap::dump(bool hash) const
1160{
1161 int leafCount = 0;
1162 JLOG(journal_.info()) << " MAP Contains";
1163
1165 stack.push({root_.get(), SHAMapNodeID()});
1166
1167 do
1168 {
1169 auto [node, nodeID] = stack.top();
1170 stack.pop();
1171
1172 JLOG(journal_.info()) << node->getString(nodeID);
1173 if (hash)
1174 {
1175 JLOG(journal_.info()) << "Hash: " << node->getHash();
1176 }
1177
1178 if (node->isInner())
1179 {
1180 auto inner = static_cast<SHAMapInnerNode*>(node);
1181 for (int i = 0; i < branchFactor; ++i)
1182 {
1183 if (!inner->isEmptyBranch(i))
1184 {
1185 auto child = inner->getChildPointer(i);
1186 if (child)
1187 {
1188 XRPL_ASSERT(
1189 child->getHash() == inner->getChildHash(i),
1190 "ripple::SHAMap::dump : child hash do match");
1191 stack.push({child, nodeID.getChildNodeID(i)});
1192 }
1193 }
1194 }
1195 }
1196 else
1197 ++leafCount;
1198 } while (!stack.empty());
1199
1200 JLOG(journal_.info()) << leafCount << " resident leaves";
1201}
1202
1205{
1206 auto ret = f_.getTreeNodeCache()->fetch(hash.as_uint256());
1207 XRPL_ASSERT(
1208 !ret || !ret->cowid(),
1209 "ripple::SHAMap::cacheLookup : not found or zero cowid");
1210 return ret;
1211}
1212
1213void
1215 SHAMapHash const& hash,
1217{
1218 XRPL_ASSERT(backed_, "ripple::SHAMap::canonicalize : is backed");
1219 XRPL_ASSERT(
1220 node->cowid() == 0, "ripple::SHAMap::canonicalize : valid node input");
1221 XRPL_ASSERT(
1222 node->getHash() == hash,
1223 "ripple::SHAMap::canonicalize : node hash do match");
1224
1225 f_.getTreeNodeCache()->canonicalize_replace_client(hash.as_uint256(), node);
1226}
1227
1228void
1230{
1231 (void)getHash(); // update node hashes
1232 auto node = root_.get();
1233 XRPL_ASSERT(node, "ripple::SHAMap::invariants : non-null root node");
1234 XRPL_ASSERT(
1235 !node->isLeaf(), "ripple::SHAMap::invariants : root node is not leaf");
1236 SharedPtrNodeStack stack;
1237 for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1238 leaf = peekNextItem(leaf->peekItem()->key(), stack))
1239 ;
1240 node->invariants(true);
1241}
1242
1243} // namespace ripple
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 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.
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
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.
bool isRoot() const
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.
Definition SHAMap.h:78
intr_ptr::SharedPtr< Node > preFlushNode(intr_ptr::SharedPtr< Node > node) const
prepare a node to be modified before flushing
Definition SHAMap.cpp:997
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
Definition SHAMap.cpp:374
bool hasItem(uint256 const &id) const
Does the tree have an item with the given ID?
Definition SHAMap.cpp:696
bool backed_
Definition SHAMap.h:92
intr_ptr::SharedPtr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
Definition SHAMap.cpp:1204
intr_ptr::SharedPtr< Node > unshareNode(intr_ptr::SharedPtr< Node >, SHAMapNodeID const &nodeID)
Unshare the node, allowing it to be modified.
Definition SHAMap.cpp:418
beast::Journal journal_
Definition SHAMap.h:81
static constexpr unsigned int leafDepth
The depth of the hash map: data is only present in the leaves.
Definition SHAMap.h:102
void dump(bool withHashes=false) const
Definition SHAMap.cpp:1159
boost::intrusive_ptr< SHAMapItem const > const & onlyBelow(SHAMapTreeNode *) const
If there is only one leaf below this node, get its contents.
Definition SHAMap.cpp:507
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:276
intr_ptr::SharedPtr< SHAMapTreeNode > root_
Definition SHAMap.h:89
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition SHAMap.cpp:598
SHAMapType const type_
Definition SHAMap.h:91
SHAMapState state_
Definition SHAMap.h:90
Family & f_
Definition SHAMap.h:80
intr_ptr::SharedPtr< SHAMapTreeNode > finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
Definition SHAMap.cpp:158
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:784
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
Definition SHAMap.cpp:112
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition SHAMap.cpp:298
SHAMapLeafNode const * peekNextItem(uint256 const &id, SharedPtrNodeStack &stack) const
Definition SHAMap.cpp:562
void canonicalize(SHAMapHash const &hash, intr_ptr::SharedPtr< SHAMapTreeNode > &) const
Definition SHAMap.cpp:1214
int walkSubTree(bool doWrite, NodeObjectType t)
Definition SHAMap.cpp:1028
const_iterator end() const
Definition SHAMap.h:742
void invariants() const
Definition SHAMap.cpp:1229
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:863
const_iterator upper_bound(uint256 const &id) const
Find the first item after the given item.
Definition SHAMap.cpp:621
intr_ptr::SharedPtr< SHAMapTreeNode > writeNode(NodeObjectType t, intr_ptr::SharedPtr< SHAMapTreeNode > node) const
write and canonicalize modified node
Definition SHAMap.cpp:976
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeNT(SHAMapHash const &hash) const
Definition SHAMap.cpp:253
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
Definition SHAMap.h:84
SHAMapHash getHash() const
Definition SHAMap.cpp:871
SHAMap()=delete
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, intr_ptr::SharedPtr< SHAMapTreeNode > terminal)
Update hashes up to the root.
Definition SHAMap.cpp:76
bool updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:883
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
Definition SHAMap.cpp:547
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNode(SHAMapHash const &hash) const
Definition SHAMap.cpp:265
intr_ptr::SharedPtr< SHAMapTreeNode > fetchNodeFromDB(SHAMapHash const &hash) const
Definition SHAMap.cpp:150
intr_ptr::SharedPtr< SHAMapTreeNode > descendNoStore(SHAMapInnerNode &, int branch) const
Definition SHAMap.cpp:331
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
Definition SHAMap.h:87
bool delItem(uint256 const &id)
Definition SHAMap.cpp:702
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
Definition SHAMap.cpp:928
const_iterator lower_bound(uint256 const &id) const
Find the object with the greatest object id smaller than the input id.
Definition SHAMap.cpp:658
SHAMapLeafNode * lastBelow(intr_ptr::SharedPtr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch=branchFactor) const
Definition SHAMap.cpp:481
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition SHAMap.cpp:70
int flushDirty(NodeObjectType t)
Flush modified nodes to the nodestore and convert them to shared.
Definition SHAMap.cpp:1021
int unshare()
Convert any modified nodes to shared.
Definition SHAMap.cpp:1014
intr_ptr::SharedPtr< SHAMapTreeNode > checkFilter(SHAMapHash const &hash, SHAMapSyncFilter *filter) const
Definition SHAMap.cpp:197
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
SHAMapLeafNode * findKey(uint256 const &id) const
Return nullptr if key not found.
Definition SHAMap.cpp:141
static constexpr unsigned int branchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map)
Definition SHAMap.h:98
SHAMapLeafNode * firstBelow(intr_ptr::SharedPtr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) const
Definition SHAMap.cpp:493
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.
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
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.
intr_ptr::SharedPtr< SHAMapLeafNode > makeTypedLeaf(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item, std::uint32_t owner)
Definition SHAMap.cpp:13
NodeObjectType
The types of node objects.
Definition NodeObject.h:13
@ hotUNKNOWN
Definition NodeObject.h:14
@ 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)
Definition Slice.h:225
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
Definition SHAMap.cpp:504
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
T pop(T... args)
T push(T... args)
T to_string(T... args)
T top(T... args)
T what(T... args)