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