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