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 
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_.getTreeNodeCache(ledgerSeq_)->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  ->canonicalize_replace_client(hash.as_uint256(), node);
1132 }
1133 
1134 void
1136 {
1137  (void)getHash(); // update node hashes
1138  auto node = root_.get();
1139  assert(node != nullptr);
1140  assert(!node->isLeaf());
1141  SharedPtrNodeStack stack;
1142  for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1143  leaf = peekNextItem(leaf->peekItem()->key(), stack))
1144  ;
1145  node->invariants(true);
1146 }
1147 
1148 } // 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:1135
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: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::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: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:539
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: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: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:613
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: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: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: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: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:941
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: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:399
ripple::SHAMap::updateGiveItem
bool updateGiveItem(std::shared_ptr< SHAMapItem const >, bool isTransaction, bool hasMeta)
Definition: SHAMap.cpp:803
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
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