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 #include <ripple/shamap/SHAMapAccountStateLeafNode.h>
23 #include <ripple/shamap/SHAMapNodeID.h>
24 #include <ripple/shamap/SHAMapSyncFilter.h>
25 #include <ripple/shamap/SHAMapTxLeafNode.h>
26 #include <ripple/shamap/SHAMapTxPlusMetaLeafNode.h>
27 
28 namespace ripple {
29 
32  SHAMapNodeType type,
34  std::uint32_t owner)
35 {
37  return std::make_shared<SHAMapTxLeafNode>(std::move(item), owner);
38 
40  return std::make_shared<SHAMapTxPlusMetaLeafNode>(
41  std::move(item), owner);
42 
44  return std::make_shared<SHAMapAccountStateLeafNode>(
45  std::move(item), owner);
46 
47  LogicError(
48  "Attempt to create leaf node of unknown type " +
50  static_cast<std::underlying_type_t<SHAMapNodeType>>(type)));
51 }
52 
54  : f_(f), journal_(f.journal()), state_(SHAMapState::Modifying), type_(t)
55 {
56  root_ = std::make_shared<SHAMapInnerNode>(cowid_);
57 }
58 
59 // The `hash` parameter is unused. It is part of the interface so it's clear
60 // from the parameters that this is the constructor to use when the hash is
61 // known. The fact that the parameter is unused is an implementation detail that
62 // should not change the interface.
64  : f_(f), journal_(f.journal()), state_(SHAMapState::Synching), type_(t)
65 {
66  root_ = std::make_shared<SHAMapInnerNode>(cowid_);
67 }
68 
70 SHAMap::snapShot(bool isMutable) const
71 {
72  auto ret = std::make_shared<SHAMap>(type_, f_);
73  SHAMap& newMap = *ret;
74 
75  if (!isMutable)
77 
78  newMap.cowid_ = cowid_ + 1;
79  newMap.ledgerSeq_ = ledgerSeq_;
80  newMap.root_ = root_;
81  newMap.backed_ = backed_;
82 
83  if ((state_ != SHAMapState::Immutable) ||
84  (newMap.state_ != SHAMapState::Immutable))
85  {
86  // If either map may change, they cannot share nodes
87  newMap.unshare();
88  }
89 
90  return ret;
91 }
92 
93 void
95  SharedPtrNodeStack& stack,
96  uint256 const& target,
98 {
99  // walk the tree up from through the inner nodes to the root_
100  // update hashes and links
101  // stack is a path of inner nodes up to, but not including, child
102  // child can be an inner node or a leaf
103 
104  assert(
107  assert(child && (child->cowid() == cowid_));
108 
109  while (!stack.empty())
110  {
111  auto node =
112  std::dynamic_pointer_cast<SHAMapInnerNode>(stack.top().first);
113  SHAMapNodeID nodeID = stack.top().second;
114  stack.pop();
115  assert(node != nullptr);
116 
117  int branch = selectBranch(nodeID, target);
118  assert(branch >= 0);
119 
120  node = unshareNode(std::move(node), nodeID);
121  node->setChild(branch, child);
122 
123  child = std::move(node);
124  }
125 }
126 
129 {
130  assert(stack == nullptr || stack->empty());
131  auto inNode = root_;
132  SHAMapNodeID nodeID;
133 
134  while (inNode->isInner())
135  {
136  if (stack != nullptr)
137  stack->push({inNode, nodeID});
138 
139  auto const inner = std::static_pointer_cast<SHAMapInnerNode>(inNode);
140  auto const branch = selectBranch(nodeID, id);
141  if (inner->isEmptyBranch(branch))
142  return nullptr;
143 
144  inNode = descendThrow(inner, branch);
145  nodeID = nodeID.getChildNodeID(branch);
146  }
147 
148  if (stack != nullptr)
149  stack->push({inNode, nodeID});
150  return static_cast<SHAMapLeafNode*>(inNode.get());
151 }
152 
154 SHAMap::findKey(uint256 const& id) const
155 {
156  SHAMapLeafNode* leaf = walkTowardsKey(id);
157  if (leaf && leaf->peekItem()->key() != id)
158  leaf = nullptr;
159  return leaf;
160 }
161 
164 {
165  assert(backed_);
166  auto obj = f_.db().fetchNodeObject(hash.as_uint256(), ledgerSeq_);
167  return finishFetch(hash, obj);
168 }
169 
172  SHAMapHash const& hash,
173  std::shared_ptr<NodeObject> const& object) const
174 {
175  assert(backed_);
176  if (!object)
177  {
178  if (full_)
179  {
180  full_ = false;
182  }
183  return {};
184  }
185 
187  try
188  {
189  node =
190  SHAMapTreeNode::makeFromPrefix(makeSlice(object->getData()), hash);
191  if (node)
192  canonicalize(hash, node);
193  return node;
194  }
195  catch (std::exception const&)
196  {
197  JLOG(journal_.warn()) << "Invalid DB node " << hash;
199  }
200 }
201 
202 // See if a sync filter has a node
205 {
206  if (auto nodeData = filter->getNode(hash))
207  {
208  try
209  {
210  auto node =
211  SHAMapTreeNode::makeFromPrefix(makeSlice(*nodeData), hash);
212  if (node)
213  {
214  filter->gotNode(
215  true,
216  hash,
217  ledgerSeq_,
218  std::move(*nodeData),
219  node->getType());
220  if (backed_)
221  canonicalize(hash, node);
222  }
223  return node;
224  }
225  catch (std::exception const& x)
226  {
227  JLOG(f_.journal().warn())
228  << "Invalid node/data, hash=" << hash << ": " << x.what();
229  }
230  }
231  return {};
232 }
233 
234 // Get a node without throwing
235 // Used on maps where missing nodes are expected
238 {
239  auto node = cacheLookup(hash);
240  if (node)
241  return node;
242 
243  if (backed_)
244  {
245  node = fetchNodeFromDB(hash);
246  if (node)
247  {
248  canonicalize(hash, node);
249  return node;
250  }
251  }
252 
253  if (filter)
254  node = checkFilter(hash, filter);
255 
256  return node;
257 }
258 
260 SHAMap::fetchNodeNT(SHAMapHash const& hash) const
261 {
262  auto node = cacheLookup(hash);
263 
264  if (!node && backed_)
265  node = fetchNodeFromDB(hash);
266 
267  return node;
268 }
269 
270 // Throw if the node is missing
272 SHAMap::fetchNode(SHAMapHash const& hash) const
273 {
274  auto node = fetchNodeNT(hash);
275 
276  if (!node)
277  Throw<SHAMapMissingNode>(type_, hash);
278 
279  return node;
280 }
281 
283 SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const
284 {
285  SHAMapTreeNode* ret = descend(parent, branch);
286 
287  if (!ret && !parent->isEmptyBranch(branch))
288  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
289 
290  return ret;
291 }
292 
295  const
296 {
297  std::shared_ptr<SHAMapTreeNode> ret = descend(parent, branch);
298 
299  if (!ret && !parent->isEmptyBranch(branch))
300  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
301 
302  return ret;
303 }
304 
306 SHAMap::descend(SHAMapInnerNode* parent, int branch) const
307 {
308  SHAMapTreeNode* ret = parent->getChildPointer(branch);
309  if (ret || !backed_)
310  return ret;
311 
313  fetchNodeNT(parent->getChildHash(branch));
314  if (!node)
315  return nullptr;
316 
317  node = parent->canonicalizeChild(branch, std::move(node));
318  return node.get();
319 }
320 
323  const
324 {
325  std::shared_ptr<SHAMapTreeNode> node = parent->getChild(branch);
326  if (node || !backed_)
327  return node;
328 
329  node = fetchNode(parent->getChildHash(branch));
330  if (!node)
331  return nullptr;
332 
333  node = parent->canonicalizeChild(branch, std::move(node));
334  return node;
335 }
336 
337 // Gets the node that would be hooked to this branch,
338 // but doesn't hook it up.
341  std::shared_ptr<SHAMapInnerNode> const& parent,
342  int branch) const
343 {
344  std::shared_ptr<SHAMapTreeNode> ret = parent->getChild(branch);
345  if (!ret && backed_)
346  ret = fetchNode(parent->getChildHash(branch));
347  return ret;
348 }
349 
352  SHAMapInnerNode* parent,
353  SHAMapNodeID const& parentID,
354  int branch,
355  SHAMapSyncFilter* filter) const
356 {
357  assert(parent->isInner());
358  assert((branch >= 0) && (branch < branchFactor));
359  assert(!parent->isEmptyBranch(branch));
360 
361  SHAMapTreeNode* child = parent->getChildPointer(branch);
362 
363  if (!child)
364  {
365  auto const& childHash = parent->getChildHash(branch);
367  fetchNodeNT(childHash, filter);
368 
369  if (childNode)
370  {
371  childNode = parent->canonicalizeChild(branch, std::move(childNode));
372  child = childNode.get();
373  }
374  }
375 
376  return std::make_pair(child, parentID.getChildNodeID(branch));
377 }
378 
381  SHAMapInnerNode* parent,
382  int branch,
383  SHAMapSyncFilter* filter,
384  bool& pending,
385  descendCallback&& callback) const
386 {
387  pending = false;
388 
389  SHAMapTreeNode* ret = parent->getChildPointer(branch);
390  if (ret)
391  return ret;
392 
393  auto const& hash = parent->getChildHash(branch);
394 
395  auto ptr = cacheLookup(hash);
396  if (!ptr)
397  {
398  if (filter)
399  ptr = checkFilter(hash, filter);
400 
401  if (!ptr && backed_)
402  {
403  f_.db().asyncFetch(
404  hash.as_uint256(),
405  ledgerSeq_,
406  [this, hash, cb{std::move(callback)}](
407  std::shared_ptr<NodeObject> const& object) {
408  auto node = finishFetch(hash, object);
409  cb(node, hash);
410  });
411  pending = true;
412  return nullptr;
413  }
414  }
415 
416  if (ptr)
417  ptr = parent->canonicalizeChild(branch, std::move(ptr));
418 
419  return ptr.get();
420 }
421 
422 template <class Node>
425 {
426  // make sure the node is suitable for the intended operation (copy on write)
427  assert(node->cowid() <= cowid_);
428  if (node->cowid() != cowid_)
429  {
430  // have a CoW
431  assert(state_ != SHAMapState::Immutable);
432  node = std::static_pointer_cast<Node>(node->clone(cowid_));
433  if (nodeID.isRoot())
434  root_ = node;
435  }
436  return node;
437 }
438 
442  SharedPtrNodeStack& stack,
443  int branch,
444  std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const&
445  loopParams) const
446 {
447  auto& [init, cmp, incr] = loopParams;
448  if (node->isLeaf())
449  {
450  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
451  stack.push({node, {leafDepth, n->peekItem()->key()}});
452  return n.get();
453  }
454  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
455  if (stack.empty())
456  stack.push({inner, SHAMapNodeID{}});
457  else
458  stack.push({inner, stack.top().second.getChildNodeID(branch)});
459  for (int i = init; cmp(i);)
460  {
461  if (!inner->isEmptyBranch(i))
462  {
463  node = descendThrow(inner, i);
464  assert(!stack.empty());
465  if (node->isLeaf())
466  {
467  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
468  stack.push({n, {leafDepth, n->peekItem()->key()}});
469  return n.get();
470  }
471  inner = std::static_pointer_cast<SHAMapInnerNode>(node);
472  stack.push({inner, stack.top().second.getChildNodeID(branch)});
473  i = init; // descend and reset loop
474  }
475  else
476  incr(i); // scan next branch
477  }
478  return nullptr;
479 }
483  SharedPtrNodeStack& stack,
484  int branch) const
485 {
486  auto init = branchFactor - 1;
487  auto cmp = [](int i) { return i >= 0; };
488  auto incr = [](int& i) { --i; };
489 
490  return belowHelper(node, stack, branch, {init, cmp, incr});
491 }
495  SharedPtrNodeStack& stack,
496  int branch) const
497 {
498  auto init = 0;
499  auto cmp = [](int i) { return i <= branchFactor; };
500  auto incr = [](int& i) { ++i; };
501 
502  return belowHelper(node, stack, branch, {init, cmp, incr});
503 }
505 
508 {
509  // If there is only one item below this node, return it
510 
511  while (!node->isLeaf())
512  {
513  SHAMapTreeNode* nextNode = nullptr;
514  auto inner = static_cast<SHAMapInnerNode*>(node);
515  for (int i = 0; i < branchFactor; ++i)
516  {
517  if (!inner->isEmptyBranch(i))
518  {
519  if (nextNode)
520  return no_item;
521 
522  nextNode = descendThrow(inner, i);
523  }
524  }
525 
526  if (!nextNode)
527  {
528  assert(false);
529  return no_item;
530  }
531 
532  node = nextNode;
533  }
534 
535  // An inner node must have at least one leaf
536  // below it, unless it's the root_
537  auto const leaf = static_cast<SHAMapLeafNode const*>(node);
538  assert(leaf->peekItem() || (leaf == root_.get()));
539  return leaf->peekItem();
540 }
541 
542 SHAMapLeafNode const*
544 {
545  assert(stack.empty());
546  SHAMapLeafNode* node = firstBelow(root_, stack);
547  if (!node)
548  {
549  while (!stack.empty())
550  stack.pop();
551  return nullptr;
552  }
553  return node;
554 }
555 
556 SHAMapLeafNode const*
558 {
559  assert(!stack.empty());
560  assert(stack.top().first->isLeaf());
561  stack.pop();
562  while (!stack.empty())
563  {
564  auto [node, nodeID] = stack.top();
565  assert(!node->isLeaf());
566  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
567  for (auto i = selectBranch(nodeID, id) + 1; i < branchFactor; ++i)
568  {
569  if (!inner->isEmptyBranch(i))
570  {
571  node = descendThrow(inner, i);
572  auto leaf = firstBelow(node, stack, i);
573  if (!leaf)
574  Throw<SHAMapMissingNode>(type_, id);
575  assert(leaf->isLeaf());
576  return leaf;
577  }
578  }
579  stack.pop();
580  }
581  // must be last item
582  return nullptr;
583 }
584 
586 SHAMap::peekItem(uint256 const& id) const
587 {
588  SHAMapLeafNode* leaf = findKey(id);
589 
590  if (!leaf)
591  return no_item;
592 
593  return leaf->peekItem();
594 }
595 
597 SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const
598 {
599  SHAMapLeafNode* leaf = findKey(id);
600 
601  if (!leaf)
602  return no_item;
603 
604  hash = leaf->getHash();
605  return leaf->peekItem();
606 }
607 
609 SHAMap::upper_bound(uint256 const& id) const
610 {
611  // Get a const_iterator to the next item in the tree after a given item
612  // item need not be in tree
613  SharedPtrNodeStack stack;
614  walkTowardsKey(id, &stack);
615  while (!stack.empty())
616  {
617  auto [node, nodeID] = stack.top();
618  if (node->isLeaf())
619  {
620  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
621  if (leaf->peekItem()->key() > id)
622  return const_iterator(
623  this, leaf->peekItem().get(), std::move(stack));
624  }
625  else
626  {
627  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
628  for (auto branch = selectBranch(nodeID, id) + 1;
629  branch < branchFactor;
630  ++branch)
631  {
632  if (!inner->isEmptyBranch(branch))
633  {
634  node = descendThrow(inner, branch);
635  auto leaf = firstBelow(node, stack, branch);
636  if (!leaf)
637  Throw<SHAMapMissingNode>(type_, id);
638  return const_iterator(
639  this, leaf->peekItem().get(), std::move(stack));
640  }
641  }
642  }
643  stack.pop();
644  }
645  return end();
646 }
648 SHAMap::lower_bound(uint256 const& id) const
649 {
650  SharedPtrNodeStack stack;
651  walkTowardsKey(id, &stack);
652  while (!stack.empty())
653  {
654  auto [node, nodeID] = stack.top();
655  if (node->isLeaf())
656  {
657  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
658  if (leaf->peekItem()->key() < id)
659  return const_iterator(
660  this, leaf->peekItem().get(), std::move(stack));
661  }
662  else
663  {
664  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
665  for (int branch = selectBranch(nodeID, id) - 1; branch >= 0;
666  --branch)
667  {
668  if (!inner->isEmptyBranch(branch))
669  {
670  node = descendThrow(inner, branch);
671  auto leaf = lastBelow(node, stack, branch);
672  if (!leaf)
673  Throw<SHAMapMissingNode>(type_, id);
674  return const_iterator(
675  this, leaf->peekItem().get(), std::move(stack));
676  }
677  }
678  }
679  stack.pop();
680  }
681  // TODO: what to return here?
682  return end();
683 }
684 
685 bool
686 SHAMap::hasItem(uint256 const& id) const
687 {
688  return (findKey(id) != nullptr);
689 }
690 
691 bool
693 {
694  // delete the item with this ID
695  assert(state_ != SHAMapState::Immutable);
696 
697  SharedPtrNodeStack stack;
698  walkTowardsKey(id, &stack);
699 
700  if (stack.empty())
701  Throw<SHAMapMissingNode>(type_, id);
702 
703  auto leaf = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
704  stack.pop();
705 
706  if (!leaf || (leaf->peekItem()->key() != id))
707  return false;
708 
709  SHAMapNodeType type = leaf->getType();
710 
711  // What gets attached to the end of the chain
712  // (For now, nothing, since we deleted the leaf)
714 
715  while (!stack.empty())
716  {
717  auto node =
718  std::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
719  SHAMapNodeID nodeID = stack.top().second;
720  stack.pop();
721 
722  node = unshareNode(std::move(node), nodeID);
723  node->setChild(selectBranch(nodeID, id), prevNode);
724 
725  if (!nodeID.isRoot())
726  {
727  // we may have made this a node with 1 or 0 children
728  // And, if so, we need to remove this branch
729  const int bc = node->getBranchCount();
730  if (bc == 0)
731  {
732  // no children below this branch
733  prevNode.reset();
734  }
735  else if (bc == 1)
736  {
737  // If there's only one item, pull up on the thread
738  auto item = onlyBelow(node.get());
739 
740  if (item)
741  {
742  for (int i = 0; i < branchFactor; ++i)
743  {
744  if (!node->isEmptyBranch(i))
745  {
746  node->setChild(i, nullptr);
747  break;
748  }
749  }
750 
751  prevNode = makeTypedLeaf(type, item, node->cowid());
752  }
753  else
754  {
755  prevNode = std::move(node);
756  }
757  }
758  else
759  {
760  // This node is now the end of the branch
761  prevNode = std::move(node);
762  }
763  }
764  }
765 
766  return true;
767 }
768 
769 bool
771 {
772  assert(state_ != SHAMapState::Immutable);
773  assert(type != SHAMapNodeType::tnINNER);
774 
775  // add the specified item, does not update
776  uint256 tag = item->key();
777 
778  SharedPtrNodeStack stack;
779  walkTowardsKey(tag, &stack);
780 
781  if (stack.empty())
782  Throw<SHAMapMissingNode>(type_, tag);
783 
784  auto [node, nodeID] = stack.top();
785  stack.pop();
786 
787  if (node->isLeaf())
788  {
789  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
790  if (leaf->peekItem()->key() == tag)
791  return false;
792  }
793  node = unshareNode(std::move(node), nodeID);
794  if (node->isInner())
795  {
796  // easy case, we end on an inner node
797  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
798  int branch = selectBranch(nodeID, tag);
799  assert(inner->isEmptyBranch(branch));
800  auto newNode = makeTypedLeaf(type, std::move(item), cowid_);
801  inner->setChild(branch, newNode);
802  }
803  else
804  {
805  // this is a leaf node that has to be made an inner node holding two
806  // items
807  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
808  std::shared_ptr<SHAMapItem const> otherItem = leaf->peekItem();
809  assert(otherItem && (tag != otherItem->key()));
810 
811  node = std::make_shared<SHAMapInnerNode>(node->cowid());
812 
813  unsigned int b1, b2;
814 
815  while ((b1 = selectBranch(nodeID, tag)) ==
816  (b2 = selectBranch(nodeID, otherItem->key())))
817  {
818  stack.push({node, nodeID});
819 
820  // we need a new inner node, since both go on same branch at this
821  // level
822  nodeID = nodeID.getChildNodeID(b1);
823  node = std::make_shared<SHAMapInnerNode>(cowid_);
824  }
825 
826  // we can add the two leaf nodes here
827  assert(node->isInner());
828 
829  auto inner = static_cast<SHAMapInnerNode*>(node.get());
830  inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
831  inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
832  }
833 
834  dirtyUp(stack, tag, node);
835  return true;
836 }
837 
838 bool
840 {
841  return addGiveItem(type, std::make_shared<SHAMapItem const>(std::move(i)));
842 }
843 
846 {
847  auto hash = root_->getHash();
848  if (hash.isZero())
849  {
850  const_cast<SHAMap&>(*this).unshare();
851  hash = root_->getHash();
852  }
853  return hash;
854 }
855 
856 bool
858  SHAMapNodeType type,
860 {
861  // can't change the tag but can change the hash
862  uint256 tag = item->key();
863 
864  assert(state_ != SHAMapState::Immutable);
865 
866  SharedPtrNodeStack stack;
867  walkTowardsKey(tag, &stack);
868 
869  if (stack.empty())
870  Throw<SHAMapMissingNode>(type_, tag);
871 
872  auto node = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
873  auto nodeID = stack.top().second;
874  stack.pop();
875 
876  if (!node || (node->peekItem()->key() != tag))
877  {
878  assert(false);
879  return false;
880  }
881 
882  if (node->getType() != type)
883  {
884  JLOG(journal_.fatal()) << "SHAMap::setItem: cross-type change!";
885  return false;
886  }
887 
888  node = unshareNode(std::move(node), nodeID);
889 
890  if (node->setItem(std::move(item)))
891  dirtyUp(stack, tag, node);
892 
893  return true;
894 }
895 
896 bool
898 {
899  if (hash == root_->getHash())
900  return true;
901 
902  if (auto stream = journal_.trace())
903  {
905  {
906  stream << "Fetch root TXN node " << hash;
907  }
908  else if (type_ == SHAMapType::STATE)
909  {
910  stream << "Fetch root STATE node " << hash;
911  }
912  else
913  {
914  stream << "Fetch root SHAMap node " << hash;
915  }
916  }
917 
918  auto newRoot = fetchNodeNT(hash, filter);
919 
920  if (newRoot)
921  {
922  root_ = newRoot;
923  assert(root_->getHash() == hash);
924  return true;
925  }
926 
927  return false;
928 }
929 
944 {
945  assert(node->cowid() == 0);
946  assert(backed_);
947 
948  canonicalize(node->getHash(), node);
949 
950  Serializer s;
951  node->serializeWithPrefix(s);
952  f_.db().store(
953  t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_);
954  return node;
955 }
956 
957 // We can't modify an inner node someone else might have a
958 // pointer to because flushing modifies inner nodes -- it
959 // makes them point to canonical/shared nodes.
960 template <class Node>
963 {
964  // A shared node should never need to be flushed
965  // because that would imply someone modified it
966  assert(node->cowid() != 0);
967 
968  if (node->cowid() != cowid_)
969  {
970  // Node is not uniquely ours, so unshare it before
971  // possibly modifying it
972  node = std::static_pointer_cast<Node>(node->clone(cowid_));
973  }
974  return node;
975 }
976 
977 int
979 {
980  // Don't share nodes with parent map
981  return walkSubTree(false, hotUNKNOWN);
982 }
983 
984 int
986 {
987  // We only write back if this map is backed.
988  return walkSubTree(backed_, t);
989 }
990 
991 int
993 {
994  assert(!doWrite || backed_);
995 
996  int flushed = 0;
997 
998  if (!root_ || (root_->cowid() == 0))
999  return flushed;
1000 
1001  if (root_->isLeaf())
1002  { // special case -- root_ is leaf
1003  root_ = preFlushNode(std::move(root_));
1004  root_->updateHash();
1005  root_->unshare();
1006 
1007  if (doWrite)
1008  root_ = writeNode(t, std::move(root_));
1009 
1010  return 1;
1011  }
1012 
1013  auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
1014 
1015  if (node->isEmpty())
1016  { // replace empty root with a new empty root
1017  root_ = std::make_shared<SHAMapInnerNode>(0);
1018  return 1;
1019  }
1020 
1021  // Stack of {parent,index,child} pointers representing
1022  // inner nodes we are in the process of flushing
1023  using StackEntry = std::pair<std::shared_ptr<SHAMapInnerNode>, int>;
1025 
1026  node = preFlushNode(std::move(node));
1027 
1028  int pos = 0;
1029 
1030  // We can't flush an inner node until we flush its children
1031  while (1)
1032  {
1033  while (pos < branchFactor)
1034  {
1035  if (node->isEmptyBranch(pos))
1036  {
1037  ++pos;
1038  }
1039  else
1040  {
1041  // No need to do I/O. If the node isn't linked,
1042  // it can't need to be flushed
1043  int branch = pos;
1044  auto child = node->getChild(pos++);
1045 
1046  if (child && (child->cowid() != 0))
1047  {
1048  // This is a node that needs to be flushed
1049 
1050  child = preFlushNode(std::move(child));
1051 
1052  if (child->isInner())
1053  {
1054  // save our place and work on this node
1055 
1056  stack.emplace(std::move(node), branch);
1057  // The semantics of this changes when we move to c++-20
1058  // Right now no move will occur; With c++-20 child will
1059  // be moved from.
1060  node = std::static_pointer_cast<SHAMapInnerNode>(
1061  std::move(child));
1062  pos = 0;
1063  }
1064  else
1065  {
1066  // flush this leaf
1067  ++flushed;
1068 
1069  assert(node->cowid() == cowid_);
1070  child->updateHash();
1071  child->unshare();
1072 
1073  if (doWrite)
1074  child = writeNode(t, std::move(child));
1075 
1076  node->shareChild(branch, child);
1077  }
1078  }
1079  }
1080  }
1081 
1082  // update the hash of this inner node
1083  node->updateHashDeep();
1084 
1085  // This inner node can now be shared
1086  node->unshare();
1087 
1088  if (doWrite)
1089  node = std::static_pointer_cast<SHAMapInnerNode>(
1090  writeNode(t, std::move(node)));
1091 
1092  ++flushed;
1093 
1094  if (stack.empty())
1095  break;
1096 
1097  auto parent = std::move(stack.top().first);
1098  pos = stack.top().second;
1099  stack.pop();
1100 
1101  // Hook this inner node to its parent
1102  assert(parent->cowid() == cowid_);
1103  parent->shareChild(pos, node);
1104 
1105  // Continue with parent's next child, if any
1106  node = std::move(parent);
1107  ++pos;
1108  }
1109 
1110  // Last inner node is the new root_
1111  root_ = std::move(node);
1112 
1113  return flushed;
1114 }
1115 
1116 void
1117 SHAMap::dump(bool hash) const
1118 {
1119  int leafCount = 0;
1120  JLOG(journal_.info()) << " MAP Contains";
1121 
1123  stack.push({root_.get(), SHAMapNodeID()});
1124 
1125  do
1126  {
1127  auto [node, nodeID] = stack.top();
1128  stack.pop();
1129 
1130  JLOG(journal_.info()) << node->getString(nodeID);
1131  if (hash)
1132  {
1133  JLOG(journal_.info()) << "Hash: " << node->getHash();
1134  }
1135 
1136  if (node->isInner())
1137  {
1138  auto inner = static_cast<SHAMapInnerNode*>(node);
1139  for (int i = 0; i < branchFactor; ++i)
1140  {
1141  if (!inner->isEmptyBranch(i))
1142  {
1143  auto child = inner->getChildPointer(i);
1144  if (child)
1145  {
1146  assert(child->getHash() == inner->getChildHash(i));
1147  stack.push({child, nodeID.getChildNodeID(i)});
1148  }
1149  }
1150  }
1151  }
1152  else
1153  ++leafCount;
1154  } while (!stack.empty());
1155 
1156  JLOG(journal_.info()) << leafCount << " resident leaves";
1157 }
1158 
1161 {
1162  auto ret = f_.getTreeNodeCache(ledgerSeq_)->fetch(hash.as_uint256());
1163  assert(!ret || !ret->cowid());
1164  return ret;
1165 }
1166 
1167 void
1169  SHAMapHash const& hash,
1170  std::shared_ptr<SHAMapTreeNode>& node) const
1171 {
1172  assert(backed_);
1173  assert(node->cowid() == 0);
1174  assert(node->getHash() == hash);
1175 
1177  ->canonicalize_replace_client(hash.as_uint256(), node);
1178 }
1179 
1180 void
1182 {
1183  (void)getHash(); // update node hashes
1184  auto node = root_.get();
1185  assert(node != nullptr);
1186  assert(!node->isLeaf());
1187  SharedPtrNodeStack stack;
1188  for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1189  leaf = peekNextItem(leaf->peekItem()->key(), stack))
1190  ;
1191  node->invariants(true);
1192 }
1193 
1194 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::SHAMapInnerNode::isInner
bool isInner() const override
Determines if this is an inner node.
Definition: SHAMapInnerNode.h:129
ripple::hotUNKNOWN
@ hotUNKNOWN
Definition: NodeObject.h:33
ripple::SHAMap::invariants
void invariants() const
Definition: SHAMap.cpp:1181
ripple::Family::getTreeNodeCache
virtual std::shared_ptr< TreeNodeCache > getTreeNodeCache(std::uint32_t ledgerSeq)=0
Return a pointer to the Family Tree Node Cache.
ripple::SHAMap::branchFactor
static constexpr unsigned int branchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map)
Definition: SHAMap.h:116
ripple::SHAMap::descendNoStore
std::shared_ptr< SHAMapTreeNode > descendNoStore(std::shared_ptr< SHAMapInnerNode > const &, int branch) const
Definition: SHAMap.cpp:340
ripple::ListDisposition::pending
@ pending
List will be valid in the future.
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:291
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::SHAMapNodeID::getChildNodeID
SHAMapNodeID getChildNodeID(unsigned int m) const
Definition: SHAMapNodeID.cpp:74
ripple::SHAMap::SHAMap
SHAMap(SHAMap const &)=delete
ripple::SHAMap::peekNextItem
SHAMapLeafNode const * peekNextItem(uint256 const &id, SharedPtrNodeStack &stack) const
Definition: SHAMap.cpp:557
ripple::SHAMapSyncFilter::getNode
virtual std::optional< Blob > getNode(SHAMapHash const &nodeHash) const =0
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:845
ripple::SHAMap::backed_
bool backed_
Definition: SHAMap.h:110
ripple::SHAMapNodeType::tnINNER
@ tnINNER
ripple::SHAMap::type_
const SHAMapType type_
Definition: SHAMap.h:109
std::exception
STL class.
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::selectBranch
unsigned int selectBranch(SHAMapNodeID const &id, uint256 const &hash)
Returns the branch that would contain the given hash.
Definition: SHAMapNodeID.cpp:121
ripple::SHAMap::fetchNodeFromDB
std::shared_ptr< SHAMapTreeNode > fetchNodeFromDB(SHAMapHash const &hash) const
Definition: SHAMap.cpp:163
ripple::Serializer::modData
Blob & modData()
Definition: Serializer.h:178
ripple::SHAMap::addItem
bool addItem(SHAMapNodeType type, SHAMapItem &&i)
Definition: SHAMap.cpp:839
ripple::SHAMapNodeType::tnACCOUNT_STATE
@ tnACCOUNT_STATE
std::pair::first
T first
ripple::SHAMapType::TRANSACTION
@ TRANSACTION
ripple::SHAMapInnerNode::canonicalizeChild
virtual std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:369
ripple::SHAMap::walkSubTree
int walkSubTree(bool doWrite, NodeObjectType t)
Definition: SHAMap.cpp:992
ripple::SHAMap::dump
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1117
ripple::NodeObjectType
NodeObjectType
The types of node objects.
Definition: NodeObject.h:32
ripple::SHAMap::onlyBelow
std::shared_ptr< SHAMapItem const > const & onlyBelow(SHAMapTreeNode *) const
If there is only one leaf below this node, get its contents.
Definition: SHAMap.cpp:507
ripple::SHAMapNodeType
SHAMapNodeType
Definition: SHAMapTreeNode.h:46
std::stack< std::pair< std::shared_ptr< SHAMapTreeNode >, SHAMapNodeID > >
ripple::SHAMap::peekItem
std::shared_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition: SHAMap.cpp:586
ripple::NodeStore::Database::asyncFetch
void asyncFetch(uint256 const &hash, std::uint32_t ledgerSeq, std::function< void(std::shared_ptr< NodeObject > const &)> &&callback)
Fetch an object without waiting.
Definition: Database.cpp:107
ripple::SHAMap::fetchNode
std::shared_ptr< SHAMapTreeNode > fetchNode(SHAMapHash const &hash) const
Definition: SHAMap.cpp:272
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::SHAMap::fetchNodeNT
std::shared_ptr< SHAMapTreeNode > fetchNodeNT(SHAMapHash const &hash) const
Definition: SHAMap.cpp:260
ripple::SHAMap::belowHelper
SHAMapLeafNode * belowHelper(std::shared_ptr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch, std::tuple< int, std::function< bool(int)>, std::function< void(int &)>> const &loopParams) const
Definition: SHAMap.cpp:440
std::tuple
ripple::SHAMapState::Modifying
@ Modifying
The map is in flux and objects can be added and removed.
std::function
ripple::NodeStore::Database::store
virtual void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t ledgerSeq)=0
Store the object.
ripple::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:33
ripple::SHAMap::snapShot
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:70
ripple::SHAMapType::STATE
@ STATE
ripple::const_iterator
Dir::const_iterator const_iterator
Definition: Directory.cpp:25
ripple::SHAMap::cacheLookup
std::shared_ptr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
Definition: SHAMap.cpp:1160
ripple::Family::missingNode
virtual void missingNode(std::uint32_t refNum)=0
ripple::Family::db
virtual NodeStore::Database & db()=0
ripple::SHAMapLeafNode
Definition: SHAMapLeafNode.h:32
std::shared_ptr::reset
T reset(T... args)
ripple::SHAMapHash
Definition: SHAMapHash.h:32
ripple::SHAMapState::Synching
@ Synching
The map's hash is fixed but valid nodes may be missing and can be added.
ripple::SHAMapNodeType::tnTRANSACTION_NM
@ tnTRANSACTION_NM
ripple::SHAMap::f_
Family & f_
Definition: SHAMap.h:98
std::underlying_type_t
ripple::SHAMap::lastBelow
SHAMapLeafNode * lastBelow(std::shared_ptr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch=branchFactor) const
Definition: SHAMap.cpp:481
ripple::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:195
ripple::base_uint< 256 >
ripple::SHAMap::hasItem
bool hasItem(uint256 const &id) const
Does the tree have an item with the given ID?
Definition: SHAMap.cpp:686
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:359
ripple::SHAMapInnerNode
Definition: SHAMapInnerNode.h:39
ripple::SHAMap::peekFirstItem
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
Definition: SHAMap.cpp:543
ripple::SHAMapItem
Definition: SHAMapItem.h:31
ripple::SHAMapSyncFilter::gotNode
virtual void gotNode(bool fromFilter, SHAMapHash const &nodeHash, std::uint32_t ledgerSeq, Blob &&nodeData, SHAMapNodeType type) const =0
ripple::no_item
static const std::shared_ptr< SHAMapItem const > no_item
Definition: SHAMap.cpp:504
ripple::SHAMap::const_iterator
Definition: SHAMap.h:624
ripple::SHAMap::firstBelow
SHAMapLeafNode * firstBelow(std::shared_ptr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) const
Definition: SHAMap.cpp:493
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:95
ripple::SHAMapNodeType::tnTRANSACTION_MD
@ tnTRANSACTION_MD
std::stack::top
T top(T... args)
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:53
std::to_string
T to_string(T... args)
ripple::SHAMap::descendThrow
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:283
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::SHAMap::journal_
beast::Journal journal_
Definition: SHAMap.h:99
ripple::SHAMapTreeNode::isLeaf
virtual bool isLeaf() const =0
Determines if this is a leaf node.
ripple::SHAMap::canonicalize
void canonicalize(SHAMapHash const &hash, std::shared_ptr< SHAMapTreeNode > &) const
Definition: SHAMap.cpp:1168
ripple::Family
Definition: Family.h:32
std::uint32_t
ripple::SHAMap::fetchRoot
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
Definition: SHAMap.cpp:897
ripple::SHAMapTreeNode::getHash
SHAMapHash const & getHash() const
Return the hash of this node.
Definition: SHAMapTreeNode.h:143
ripple::Serializer
Definition: Serializer.h:39
ripple::SHAMapTreeNode::makeFromPrefix
static std::shared_ptr< SHAMapTreeNode > makeFromPrefix(Slice rawNode, SHAMapHash const &hash)
Definition: SHAMapTreeNode.cpp:148
ripple::SHAMap::upper_bound
const_iterator upper_bound(uint256 const &id) const
Definition: SHAMap.cpp:609
ripple::SHAMap::finishFetch
std::shared_ptr< SHAMapTreeNode > finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
Definition: SHAMap.cpp:171
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:111
ripple::SHAMap::checkFilter
std::shared_ptr< SHAMapTreeNode > checkFilter(SHAMapHash const &hash, SHAMapSyncFilter *filter) const
Definition: SHAMap.cpp:204
ripple::SHAMap::dirtyUp
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, std::shared_ptr< SHAMapTreeNode > terminal)
Update hashes up to the root.
Definition: SHAMap.cpp:94
ripple::SHAMap::findKey
SHAMapLeafNode * findKey(uint256 const &id) const
Return nullptr if key not found.
Definition: SHAMap.cpp:154
ripple::SHAMap::updateGiveItem
bool updateGiveItem(SHAMapNodeType type, std::shared_ptr< SHAMapItem const >)
Definition: SHAMap.cpp:857
ripple::makeTypedLeaf
std::shared_ptr< SHAMapLeafNode > makeTypedLeaf(SHAMapNodeType type, std::shared_ptr< SHAMapItem const > item, std::uint32_t owner)
Definition: SHAMap.cpp:31
ripple::SHAMap::end
const_iterator end() const
Definition: SHAMap.h:738
ripple::SHAMap::walkTowardsKey
SHAMapLeafNode * walkTowardsKey(uint256 const &id, SharedPtrNodeStack *stack=nullptr) const
Walk towards the specified id, returning the node.
Definition: SHAMap.cpp:128
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
ripple::SHAMap::addGiveItem
bool addGiveItem(SHAMapNodeType type, std::shared_ptr< SHAMapItem const > item)
Definition: SHAMap.cpp:770
ripple::SHAMap::unshare
int unshare()
Convert any modified nodes to shared.
Definition: SHAMap.cpp:978
ripple::SHAMap::descendAsync
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
Definition: SHAMap.cpp:380
ripple::SHAMap::writeNode
std::shared_ptr< SHAMapTreeNode > writeNode(NodeObjectType t, std::shared_ptr< SHAMapTreeNode > node) const
write and canonicalize modified node
Definition: SHAMap.cpp:943
ripple::SHAMap::leafDepth
static constexpr unsigned int leafDepth
The depth of the hash map: data is only present in the leaves.
Definition: SHAMap.h:120
std::stack::empty
T empty(T... args)
ripple::SHAMapState::Immutable
@ Immutable
The map is set in stone and cannot be changed.
std::stack::push
T push(T... args)
ripple::SHAMapType
SHAMapType
Definition: SHAMapMissingNode.h:32
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:962
ripple::NodeStore::Database::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous, bool duplicate=false)
Fetch a node object.
Definition: Database.cpp:158
ripple::SHAMap::flushDirty
int flushDirty(NodeObjectType t)
Flush modified nodes to the nodestore and convert them to shared.
Definition: SHAMap.cpp:985
ripple::Family::journal
virtual beast::Journal const & journal()=0
ripple::SHAMap::lower_bound
const_iterator lower_bound(uint256 const &id) const
Definition: SHAMap.cpp:648
ripple::SHAMapLeafNode::peekItem
std::shared_ptr< SHAMapItem const > const & peekItem() const
Definition: SHAMapLeafNode.cpp:44
ripple::SHAMapHash::as_uint256
uint256 const & as_uint256() const
Definition: SHAMapHash.h:43
ripple::SHAMapInnerNode::getChildPointer
SHAMapTreeNode * getChildPointer(int branch)
Definition: SHAMapInnerNode.cpp:339
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:424
ripple::SHAMap::descend
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:306
ripple::SHAMap::ledgerSeq_
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
Definition: SHAMap.h:105
ripple::SHAMapSyncFilter
Definition: SHAMapSyncFilter.h:30
ripple::SHAMap::delItem
bool delItem(uint256 const &id)
Definition: SHAMap.cpp:692
ripple::SHAMap::cowid_
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
Definition: SHAMap.h:102
ripple::SHAMap::state_
SHAMapState state_
Definition: SHAMap.h:108
ripple::SHAMapNodeID::isRoot
bool isRoot() const
Definition: SHAMapNodeID.h:48
std::exception::what
T what(T... args)
ripple::SHAMap::root_
std::shared_ptr< SHAMapTreeNode > root_
Definition: SHAMap.h:107
ripple::SHAMapState
SHAMapState
Describes the current state of a given SHAMap.
Definition: SHAMap.h:46