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