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