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