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