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