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