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  SharedPtrNodeStack stack;
612  walkTowardsKey(id, &stack);
613  while (!stack.empty())
614  {
615  auto [node, nodeID] = stack.top();
616  if (node->isLeaf())
617  {
618  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
619  if (leaf->peekItem()->key() > id)
620  return const_iterator(
621  this, leaf->peekItem().get(), std::move(stack));
622  }
623  else
624  {
625  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
626  for (auto branch = selectBranch(nodeID, id) + 1;
627  branch < branchFactor;
628  ++branch)
629  {
630  if (!inner->isEmptyBranch(branch))
631  {
632  node = descendThrow(inner, branch);
633  auto leaf = firstBelow(node, stack, branch);
634  if (!leaf)
635  Throw<SHAMapMissingNode>(type_, id);
636  return const_iterator(
637  this, leaf->peekItem().get(), std::move(stack));
638  }
639  }
640  }
641  stack.pop();
642  }
643  return end();
644 }
646 SHAMap::lower_bound(uint256 const& id) const
647 {
648  SharedPtrNodeStack stack;
649  walkTowardsKey(id, &stack);
650  while (!stack.empty())
651  {
652  auto [node, nodeID] = stack.top();
653  if (node->isLeaf())
654  {
655  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
656  if (leaf->peekItem()->key() < id)
657  return const_iterator(
658  this, leaf->peekItem().get(), std::move(stack));
659  }
660  else
661  {
662  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
663  for (int branch = selectBranch(nodeID, id) - 1; branch >= 0;
664  --branch)
665  {
666  if (!inner->isEmptyBranch(branch))
667  {
668  node = descendThrow(inner, branch);
669  auto leaf = lastBelow(node, stack, branch);
670  if (!leaf)
671  Throw<SHAMapMissingNode>(type_, id);
672  return const_iterator(
673  this, leaf->peekItem().get(), std::move(stack));
674  }
675  }
676  }
677  stack.pop();
678  }
679  // TODO: what to return here?
680  return end();
681 }
682 
683 bool
684 SHAMap::hasItem(uint256 const& id) const
685 {
686  return (findKey(id) != nullptr);
687 }
688 
689 bool
691 {
692  // delete the item with this ID
693  assert(state_ != SHAMapState::Immutable);
694 
695  SharedPtrNodeStack stack;
696  walkTowardsKey(id, &stack);
697 
698  if (stack.empty())
699  Throw<SHAMapMissingNode>(type_, id);
700 
701  auto leaf = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
702  stack.pop();
703 
704  if (!leaf || (leaf->peekItem()->key() != id))
705  return false;
706 
707  SHAMapNodeType type = leaf->getType();
708 
709  // What gets attached to the end of the chain
710  // (For now, nothing, since we deleted the leaf)
712 
713  while (!stack.empty())
714  {
715  auto node =
716  std::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
717  SHAMapNodeID nodeID = stack.top().second;
718  stack.pop();
719 
720  node = unshareNode(std::move(node), nodeID);
721  node->setChild(selectBranch(nodeID, id), prevNode);
722 
723  if (!nodeID.isRoot())
724  {
725  // we may have made this a node with 1 or 0 children
726  // And, if so, we need to remove this branch
727  const int bc = node->getBranchCount();
728  if (bc == 0)
729  {
730  // no children below this branch
731  prevNode.reset();
732  }
733  else if (bc == 1)
734  {
735  // If there's only one item, pull up on the thread
736  auto item = onlyBelow(node.get());
737 
738  if (item)
739  {
740  for (int i = 0; i < branchFactor; ++i)
741  {
742  if (!node->isEmptyBranch(i))
743  {
744  node->setChild(i, nullptr);
745  break;
746  }
747  }
748 
749  prevNode = makeTypedLeaf(type, item, node->cowid());
750  }
751  else
752  {
753  prevNode = std::move(node);
754  }
755  }
756  else
757  {
758  // This node is now the end of the branch
759  prevNode = std::move(node);
760  }
761  }
762  }
763 
764  return true;
765 }
766 
767 bool
769 {
770  assert(state_ != SHAMapState::Immutable);
771  assert(type != SHAMapNodeType::tnINNER);
772 
773  // add the specified item, does not update
774  uint256 tag = item->key();
775 
776  SharedPtrNodeStack stack;
777  walkTowardsKey(tag, &stack);
778 
779  if (stack.empty())
780  Throw<SHAMapMissingNode>(type_, tag);
781 
782  auto [node, nodeID] = stack.top();
783  stack.pop();
784 
785  if (node->isLeaf())
786  {
787  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
788  if (leaf->peekItem()->key() == tag)
789  return false;
790  }
791  node = unshareNode(std::move(node), nodeID);
792  if (node->isInner())
793  {
794  // easy case, we end on an inner node
795  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
796  int branch = selectBranch(nodeID, tag);
797  assert(inner->isEmptyBranch(branch));
798  auto newNode = makeTypedLeaf(type, std::move(item), cowid_);
799  inner->setChild(branch, newNode);
800  }
801  else
802  {
803  // this is a leaf node that has to be made an inner node holding two
804  // items
805  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
806  std::shared_ptr<SHAMapItem const> otherItem = leaf->peekItem();
807  assert(otherItem && (tag != otherItem->key()));
808 
809  node = std::make_shared<SHAMapInnerNode>(node->cowid());
810 
811  unsigned int b1, b2;
812 
813  while ((b1 = selectBranch(nodeID, tag)) ==
814  (b2 = selectBranch(nodeID, otherItem->key())))
815  {
816  stack.push({node, nodeID});
817 
818  // we need a new inner node, since both go on same branch at this
819  // level
820  nodeID = nodeID.getChildNodeID(b1);
821  node = std::make_shared<SHAMapInnerNode>(cowid_);
822  }
823 
824  // we can add the two leaf nodes here
825  assert(node->isInner());
826 
827  auto inner = static_cast<SHAMapInnerNode*>(node.get());
828  inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
829  inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
830  }
831 
832  dirtyUp(stack, tag, node);
833  return true;
834 }
835 
836 bool
838 {
839  return addGiveItem(type, std::make_shared<SHAMapItem const>(std::move(i)));
840 }
841 
844 {
845  auto hash = root_->getHash();
846  if (hash.isZero())
847  {
848  const_cast<SHAMap&>(*this).unshare();
849  hash = root_->getHash();
850  }
851  return hash;
852 }
853 
854 bool
856  SHAMapNodeType type,
858 {
859  // can't change the tag but can change the hash
860  uint256 tag = item->key();
861 
862  assert(state_ != SHAMapState::Immutable);
863 
864  SharedPtrNodeStack stack;
865  walkTowardsKey(tag, &stack);
866 
867  if (stack.empty())
868  Throw<SHAMapMissingNode>(type_, tag);
869 
870  auto node = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
871  auto nodeID = stack.top().second;
872  stack.pop();
873 
874  if (!node || (node->peekItem()->key() != tag))
875  {
876  assert(false);
877  return false;
878  }
879 
880  if (node->getType() != type)
881  {
882  JLOG(journal_.fatal()) << "SHAMap::setItem: cross-type change!";
883  return false;
884  }
885 
886  node = unshareNode(std::move(node), nodeID);
887 
888  if (node->setItem(std::move(item)))
889  dirtyUp(stack, tag, node);
890 
891  return true;
892 }
893 
894 bool
896 {
897  if (hash == root_->getHash())
898  return true;
899 
900  if (auto stream = journal_.trace())
901  {
903  {
904  stream << "Fetch root TXN node " << hash;
905  }
906  else if (type_ == SHAMapType::STATE)
907  {
908  stream << "Fetch root STATE node " << hash;
909  }
910  else
911  {
912  stream << "Fetch root SHAMap node " << hash;
913  }
914  }
915 
916  auto newRoot = fetchNodeNT(hash, filter);
917 
918  if (newRoot)
919  {
920  root_ = newRoot;
921  assert(root_->getHash() == hash);
922  return true;
923  }
924 
925  return false;
926 }
927 
942 {
943  assert(node->cowid() == 0);
944  assert(backed_);
945 
946  canonicalize(node->getHash(), node);
947 
948  Serializer s;
949  node->serializeWithPrefix(s);
950  f_.db().store(
951  t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_);
952  return node;
953 }
954 
955 // We can't modify an inner node someone else might have a
956 // pointer to because flushing modifies inner nodes -- it
957 // makes them point to canonical/shared nodes.
958 template <class Node>
961 {
962  // A shared node should never need to be flushed
963  // because that would imply someone modified it
964  assert(node->cowid() != 0);
965 
966  if (node->cowid() != cowid_)
967  {
968  // Node is not uniquely ours, so unshare it before
969  // possibly modifying it
970  node = std::static_pointer_cast<Node>(node->clone(cowid_));
971  }
972  return node;
973 }
974 
975 int
977 {
978  // Don't share nodes with parent map
979  return walkSubTree(false, hotUNKNOWN);
980 }
981 
982 int
984 {
985  // We only write back if this map is backed.
986  return walkSubTree(backed_, t);
987 }
988 
989 int
991 {
992  assert(!doWrite || backed_);
993 
994  int flushed = 0;
995 
996  if (!root_ || (root_->cowid() == 0))
997  return flushed;
998 
999  if (root_->isLeaf())
1000  { // special case -- root_ is leaf
1001  root_ = preFlushNode(std::move(root_));
1002  root_->updateHash();
1003  root_->unshare();
1004 
1005  if (doWrite)
1006  root_ = writeNode(t, std::move(root_));
1007 
1008  return 1;
1009  }
1010 
1011  auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
1012 
1013  if (node->isEmpty())
1014  { // replace empty root with a new empty root
1015  root_ = std::make_shared<SHAMapInnerNode>(0);
1016  return 1;
1017  }
1018 
1019  // Stack of {parent,index,child} pointers representing
1020  // inner nodes we are in the process of flushing
1021  using StackEntry = std::pair<std::shared_ptr<SHAMapInnerNode>, int>;
1023 
1024  node = preFlushNode(std::move(node));
1025 
1026  int pos = 0;
1027 
1028  // We can't flush an inner node until we flush its children
1029  while (1)
1030  {
1031  while (pos < branchFactor)
1032  {
1033  if (node->isEmptyBranch(pos))
1034  {
1035  ++pos;
1036  }
1037  else
1038  {
1039  // No need to do I/O. If the node isn't linked,
1040  // it can't need to be flushed
1041  int branch = pos;
1042  auto child = node->getChild(pos++);
1043 
1044  if (child && (child->cowid() != 0))
1045  {
1046  // This is a node that needs to be flushed
1047 
1048  child = preFlushNode(std::move(child));
1049 
1050  if (child->isInner())
1051  {
1052  // save our place and work on this node
1053 
1054  stack.emplace(std::move(node), branch);
1055  // The semantics of this changes when we move to c++-20
1056  // Right now no move will occur; With c++-20 child will
1057  // be moved from.
1058  node = std::static_pointer_cast<SHAMapInnerNode>(
1059  std::move(child));
1060  pos = 0;
1061  }
1062  else
1063  {
1064  // flush this leaf
1065  ++flushed;
1066 
1067  assert(node->cowid() == cowid_);
1068  child->updateHash();
1069  child->unshare();
1070 
1071  if (doWrite)
1072  child = writeNode(t, std::move(child));
1073 
1074  node->shareChild(branch, child);
1075  }
1076  }
1077  }
1078  }
1079 
1080  // update the hash of this inner node
1081  node->updateHashDeep();
1082 
1083  // This inner node can now be shared
1084  node->unshare();
1085 
1086  if (doWrite)
1087  node = std::static_pointer_cast<SHAMapInnerNode>(
1088  writeNode(t, std::move(node)));
1089 
1090  ++flushed;
1091 
1092  if (stack.empty())
1093  break;
1094 
1095  auto parent = std::move(stack.top().first);
1096  pos = stack.top().second;
1097  stack.pop();
1098 
1099  // Hook this inner node to its parent
1100  assert(parent->cowid() == cowid_);
1101  parent->shareChild(pos, node);
1102 
1103  // Continue with parent's next child, if any
1104  node = std::move(parent);
1105  ++pos;
1106  }
1107 
1108  // Last inner node is the new root_
1109  root_ = std::move(node);
1110 
1111  return flushed;
1112 }
1113 
1114 void
1115 SHAMap::dump(bool hash) const
1116 {
1117  int leafCount = 0;
1118  JLOG(journal_.info()) << " MAP Contains";
1119 
1121  stack.push({root_.get(), SHAMapNodeID()});
1122 
1123  do
1124  {
1125  auto [node, nodeID] = stack.top();
1126  stack.pop();
1127 
1128  JLOG(journal_.info()) << node->getString(nodeID);
1129  if (hash)
1130  {
1131  JLOG(journal_.info()) << "Hash: " << node->getHash();
1132  }
1133 
1134  if (node->isInner())
1135  {
1136  auto inner = static_cast<SHAMapInnerNode*>(node);
1137  for (int i = 0; i < branchFactor; ++i)
1138  {
1139  if (!inner->isEmptyBranch(i))
1140  {
1141  auto child = inner->getChildPointer(i);
1142  if (child)
1143  {
1144  assert(child->getHash() == inner->getChildHash(i));
1145  stack.push({child, nodeID.getChildNodeID(i)});
1146  }
1147  }
1148  }
1149  }
1150  else
1151  ++leafCount;
1152  } while (!stack.empty());
1153 
1154  JLOG(journal_.info()) << leafCount << " resident leaves";
1155 }
1156 
1159 {
1160  auto ret = f_.getTreeNodeCache(ledgerSeq_)->fetch(hash.as_uint256());
1161  assert(!ret || !ret->cowid());
1162  return ret;
1163 }
1164 
1165 void
1167  SHAMapHash const& hash,
1168  std::shared_ptr<SHAMapTreeNode>& node) const
1169 {
1170  assert(backed_);
1171  assert(node->cowid() == 0);
1172  assert(node->getHash() == hash);
1173 
1175  ->canonicalize_replace_client(hash.as_uint256(), node);
1176 }
1177 
1178 void
1180 {
1181  (void)getHash(); // update node hashes
1182  auto node = root_.get();
1183  assert(node != nullptr);
1184  assert(!node->isLeaf());
1185  SharedPtrNodeStack stack;
1186  for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1187  leaf = peekNextItem(leaf->peekItem()->key(), stack))
1188  ;
1189  node->invariants(true);
1190 }
1191 
1192 } // 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:132
ripple::hotUNKNOWN
@ hotUNKNOWN
Definition: NodeObject.h:33
ripple::SHAMap::invariants
void invariants() const
Definition: SHAMap.cpp:1179
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:287
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:241
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:843
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:837
ripple::SHAMapNodeType::tnACCOUNT_STATE
@ tnACCOUNT_STATE
std::pair::first
T first
ripple::SHAMapType::TRANSACTION
@ TRANSACTION
ripple::SHAMapInnerNode::canonicalizeChild
std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:371
ripple::SHAMap::walkSubTree
int walkSubTree(bool doWrite, NodeObjectType t)
Definition: SHAMap.cpp:990
ripple::SHAMap::dump
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1115
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
virtual 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:198
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:24
ripple::SHAMap::cacheLookup
std::shared_ptr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
Definition: SHAMap.cpp:1158
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:198
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:684
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:361
ripple::SHAMapInnerNode
Definition: SHAMapInnerNode.h:41
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:630
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:1166
ripple::Family
Definition: Family.h:32
std::uint32_t
ripple::SHAMap::fetchRoot
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
Definition: SHAMap.cpp:895
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
Find the first item after the given item.
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:855
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:744
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:768
ripple::SHAMap::unshare
int unshare()
Convert any modified nodes to shared.
Definition: SHAMap.cpp:976
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:941
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:960
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:252
ripple::SHAMap::flushDirty
int flushDirty(NodeObjectType t)
Flush modified nodes to the nodestore and convert them to shared.
Definition: SHAMap.cpp:983
ripple::Family::journal
virtual beast::Journal const & journal()=0
ripple::SHAMap::lower_bound
const_iterator lower_bound(uint256 const &id) const
Find the object with the greatest object id smaller than the input id.
Definition: SHAMap.cpp:646
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:335
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:690
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