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, std::move(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 
178  try
179  {
180  if (!object)
181  {
182  if (full_)
183  {
184  full_ = false;
186  }
187  return {};
188  }
189 
190  node =
191  SHAMapTreeNode::makeFromPrefix(makeSlice(object->getData()), hash);
192  if (node)
193  canonicalize(hash, node);
194  return node;
195  }
196  catch (SHAMapMissingNode const& e)
197  {
198  JLOG(journal_.warn()) << "Missing node: " << hash << " : " << e.what();
199  }
200  catch (std::runtime_error const& e)
201  {
202  JLOG(journal_.warn()) << e.what();
203  }
204  catch (...)
205  {
206  JLOG(journal_.warn()) << "Invalid DB node " << hash;
207  }
208 
210 }
211 
212 // See if a sync filter has a node
215 {
216  if (auto nodeData = filter->getNode(hash))
217  {
218  try
219  {
220  auto node =
221  SHAMapTreeNode::makeFromPrefix(makeSlice(*nodeData), hash);
222  if (node)
223  {
224  filter->gotNode(
225  true,
226  hash,
227  ledgerSeq_,
228  std::move(*nodeData),
229  node->getType());
230  if (backed_)
231  canonicalize(hash, node);
232  }
233  return node;
234  }
235  catch (std::exception const& x)
236  {
237  JLOG(f_.journal().warn())
238  << "Invalid node/data, hash=" << hash << ": " << x.what();
239  }
240  }
241  return {};
242 }
243 
244 // Get a node without throwing
245 // Used on maps where missing nodes are expected
248 {
249  auto node = cacheLookup(hash);
250  if (node)
251  return node;
252 
253  if (backed_)
254  {
255  node = fetchNodeFromDB(hash);
256  if (node)
257  {
258  canonicalize(hash, node);
259  return node;
260  }
261  }
262 
263  if (filter)
264  node = checkFilter(hash, filter);
265 
266  return node;
267 }
268 
270 SHAMap::fetchNodeNT(SHAMapHash const& hash) const
271 {
272  auto node = cacheLookup(hash);
273 
274  if (!node && backed_)
275  node = fetchNodeFromDB(hash);
276 
277  return node;
278 }
279 
280 // Throw if the node is missing
282 SHAMap::fetchNode(SHAMapHash const& hash) const
283 {
284  auto node = fetchNodeNT(hash);
285 
286  if (!node)
287  Throw<SHAMapMissingNode>(type_, hash);
288 
289  return node;
290 }
291 
293 SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const
294 {
295  SHAMapTreeNode* ret = descend(parent, branch);
296 
297  if (!ret && !parent->isEmptyBranch(branch))
298  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
299 
300  return ret;
301 }
302 
305  const
306 {
307  std::shared_ptr<SHAMapTreeNode> ret = descend(parent, branch);
308 
309  if (!ret && !parent->isEmptyBranch(branch))
310  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
311 
312  return ret;
313 }
314 
316 SHAMap::descend(SHAMapInnerNode* parent, int branch) const
317 {
318  SHAMapTreeNode* ret = parent->getChildPointer(branch);
319  if (ret || !backed_)
320  return ret;
321 
323  fetchNodeNT(parent->getChildHash(branch));
324  if (!node)
325  return nullptr;
326 
327  node = parent->canonicalizeChild(branch, std::move(node));
328  return node.get();
329 }
330 
333  const
334 {
335  std::shared_ptr<SHAMapTreeNode> node = parent->getChild(branch);
336  if (node || !backed_)
337  return node;
338 
339  node = fetchNode(parent->getChildHash(branch));
340  if (!node)
341  return nullptr;
342 
343  node = parent->canonicalizeChild(branch, std::move(node));
344  return node;
345 }
346 
347 // Gets the node that would be hooked to this branch,
348 // but doesn't hook it up.
351  std::shared_ptr<SHAMapInnerNode> const& parent,
352  int branch) const
353 {
354  std::shared_ptr<SHAMapTreeNode> ret = parent->getChild(branch);
355  if (!ret && backed_)
356  ret = fetchNode(parent->getChildHash(branch));
357  return ret;
358 }
359 
362  SHAMapInnerNode* parent,
363  SHAMapNodeID const& parentID,
364  int branch,
365  SHAMapSyncFilter* filter) const
366 {
367  assert(parent->isInner());
368  assert((branch >= 0) && (branch < branchFactor));
369  assert(!parent->isEmptyBranch(branch));
370 
371  SHAMapTreeNode* child = parent->getChildPointer(branch);
372 
373  if (!child)
374  {
375  auto const& childHash = parent->getChildHash(branch);
377  fetchNodeNT(childHash, filter);
378 
379  if (childNode)
380  {
381  childNode = parent->canonicalizeChild(branch, std::move(childNode));
382  child = childNode.get();
383  }
384  }
385 
386  return std::make_pair(child, parentID.getChildNodeID(branch));
387 }
388 
391  SHAMapInnerNode* parent,
392  int branch,
393  SHAMapSyncFilter* filter,
394  bool& pending,
395  descendCallback&& callback) const
396 {
397  pending = false;
398 
399  SHAMapTreeNode* ret = parent->getChildPointer(branch);
400  if (ret)
401  return ret;
402 
403  auto const& hash = parent->getChildHash(branch);
404 
405  auto ptr = cacheLookup(hash);
406  if (!ptr)
407  {
408  if (filter)
409  ptr = checkFilter(hash, filter);
410 
411  if (!ptr && backed_)
412  {
413  f_.db().asyncFetch(
414  hash.as_uint256(),
415  ledgerSeq_,
416  [this, hash, cb{std::move(callback)}](
417  std::shared_ptr<NodeObject> const& object) {
418  auto node = finishFetch(hash, object);
419  cb(node, hash);
420  });
421  pending = true;
422  return nullptr;
423  }
424  }
425 
426  if (ptr)
427  ptr = parent->canonicalizeChild(branch, std::move(ptr));
428 
429  return ptr.get();
430 }
431 
432 template <class Node>
435 {
436  // make sure the node is suitable for the intended operation (copy on write)
437  assert(node->cowid() <= cowid_);
438  if (node->cowid() != cowid_)
439  {
440  // have a CoW
441  assert(state_ != SHAMapState::Immutable);
442  node = std::static_pointer_cast<Node>(node->clone(cowid_));
443  if (nodeID.isRoot())
444  root_ = node;
445  }
446  return node;
447 }
448 
452  SharedPtrNodeStack& stack,
453  int branch,
454  std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const&
455  loopParams) const
456 {
457  auto& [init, cmp, incr] = loopParams;
458  if (node->isLeaf())
459  {
460  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
461  stack.push({node, {leafDepth, n->peekItem()->key()}});
462  return n.get();
463  }
464  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
465  if (stack.empty())
466  stack.push({inner, SHAMapNodeID{}});
467  else
468  stack.push({inner, stack.top().second.getChildNodeID(branch)});
469  for (int i = init; cmp(i);)
470  {
471  if (!inner->isEmptyBranch(i))
472  {
473  node = descendThrow(inner, i);
474  assert(!stack.empty());
475  if (node->isLeaf())
476  {
477  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
478  stack.push({n, {leafDepth, n->peekItem()->key()}});
479  return n.get();
480  }
481  inner = std::static_pointer_cast<SHAMapInnerNode>(node);
482  stack.push({inner, stack.top().second.getChildNodeID(branch)});
483  i = init; // descend and reset loop
484  }
485  else
486  incr(i); // scan next branch
487  }
488  return nullptr;
489 }
493  SharedPtrNodeStack& stack,
494  int branch) const
495 {
496  auto init = branchFactor - 1;
497  auto cmp = [](int i) { return i >= 0; };
498  auto incr = [](int& i) { --i; };
499 
500  return belowHelper(node, stack, branch, {init, cmp, incr});
501 }
505  SharedPtrNodeStack& stack,
506  int branch) const
507 {
508  auto init = 0;
509  auto cmp = [](int i) { return i <= branchFactor; };
510  auto incr = [](int& i) { ++i; };
511 
512  return belowHelper(node, stack, branch, {init, cmp, incr});
513 }
515 
518 {
519  // If there is only one item below this node, return it
520 
521  while (!node->isLeaf())
522  {
523  SHAMapTreeNode* nextNode = nullptr;
524  auto inner = static_cast<SHAMapInnerNode*>(node);
525  for (int i = 0; i < branchFactor; ++i)
526  {
527  if (!inner->isEmptyBranch(i))
528  {
529  if (nextNode)
530  return no_item;
531 
532  nextNode = descendThrow(inner, i);
533  }
534  }
535 
536  if (!nextNode)
537  {
538  assert(false);
539  return no_item;
540  }
541 
542  node = nextNode;
543  }
544 
545  // An inner node must have at least one leaf
546  // below it, unless it's the root_
547  auto const leaf = static_cast<SHAMapLeafNode const*>(node);
548  assert(leaf->peekItem() || (leaf == root_.get()));
549  return leaf->peekItem();
550 }
551 
552 SHAMapLeafNode const*
554 {
555  assert(stack.empty());
556  SHAMapLeafNode* node = firstBelow(root_, stack);
557  if (!node)
558  {
559  while (!stack.empty())
560  stack.pop();
561  return nullptr;
562  }
563  return node;
564 }
565 
566 SHAMapLeafNode const*
568 {
569  assert(!stack.empty());
570  assert(stack.top().first->isLeaf());
571  stack.pop();
572  while (!stack.empty())
573  {
574  auto [node, nodeID] = stack.top();
575  assert(!node->isLeaf());
576  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
577  for (auto i = selectBranch(nodeID, id) + 1; i < branchFactor; ++i)
578  {
579  if (!inner->isEmptyBranch(i))
580  {
581  node = descendThrow(inner, i);
582  auto leaf = firstBelow(node, stack, i);
583  if (!leaf)
584  Throw<SHAMapMissingNode>(type_, id);
585  assert(leaf->isLeaf());
586  return leaf;
587  }
588  }
589  stack.pop();
590  }
591  // must be last item
592  return nullptr;
593 }
594 
596 SHAMap::peekItem(uint256 const& id) const
597 {
598  SHAMapLeafNode* leaf = findKey(id);
599 
600  if (!leaf)
601  return no_item;
602 
603  return leaf->peekItem();
604 }
605 
607 SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const
608 {
609  SHAMapLeafNode* leaf = findKey(id);
610 
611  if (!leaf)
612  return no_item;
613 
614  hash = leaf->getHash();
615  return leaf->peekItem();
616 }
617 
619 SHAMap::upper_bound(uint256 const& id) const
620 {
621  SharedPtrNodeStack stack;
622  walkTowardsKey(id, &stack);
623  while (!stack.empty())
624  {
625  auto [node, nodeID] = stack.top();
626  if (node->isLeaf())
627  {
628  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
629  if (leaf->peekItem()->key() > id)
630  return const_iterator(
631  this, leaf->peekItem().get(), std::move(stack));
632  }
633  else
634  {
635  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
636  for (auto branch = selectBranch(nodeID, id) + 1;
637  branch < branchFactor;
638  ++branch)
639  {
640  if (!inner->isEmptyBranch(branch))
641  {
642  node = descendThrow(inner, branch);
643  auto leaf = firstBelow(node, stack, branch);
644  if (!leaf)
645  Throw<SHAMapMissingNode>(type_, id);
646  return const_iterator(
647  this, leaf->peekItem().get(), std::move(stack));
648  }
649  }
650  }
651  stack.pop();
652  }
653  return end();
654 }
656 SHAMap::lower_bound(uint256 const& id) const
657 {
658  SharedPtrNodeStack stack;
659  walkTowardsKey(id, &stack);
660  while (!stack.empty())
661  {
662  auto [node, nodeID] = stack.top();
663  if (node->isLeaf())
664  {
665  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
666  if (leaf->peekItem()->key() < id)
667  return const_iterator(
668  this, leaf->peekItem().get(), std::move(stack));
669  }
670  else
671  {
672  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
673  for (int branch = selectBranch(nodeID, id) - 1; branch >= 0;
674  --branch)
675  {
676  if (!inner->isEmptyBranch(branch))
677  {
678  node = descendThrow(inner, branch);
679  auto leaf = lastBelow(node, stack, branch);
680  if (!leaf)
681  Throw<SHAMapMissingNode>(type_, id);
682  return const_iterator(
683  this, leaf->peekItem().get(), std::move(stack));
684  }
685  }
686  }
687  stack.pop();
688  }
689  // TODO: what to return here?
690  return end();
691 }
692 
693 bool
694 SHAMap::hasItem(uint256 const& id) const
695 {
696  return (findKey(id) != nullptr);
697 }
698 
699 bool
701 {
702  // delete the item with this ID
703  assert(state_ != SHAMapState::Immutable);
704 
705  SharedPtrNodeStack stack;
706  walkTowardsKey(id, &stack);
707 
708  if (stack.empty())
709  Throw<SHAMapMissingNode>(type_, id);
710 
711  auto leaf = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
712  stack.pop();
713 
714  if (!leaf || (leaf->peekItem()->key() != id))
715  return false;
716 
717  SHAMapNodeType type = leaf->getType();
718 
719  // What gets attached to the end of the chain
720  // (For now, nothing, since we deleted the leaf)
722 
723  while (!stack.empty())
724  {
725  auto node =
726  std::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
727  SHAMapNodeID nodeID = stack.top().second;
728  stack.pop();
729 
730  node = unshareNode(std::move(node), nodeID);
731  node->setChild(selectBranch(nodeID, id), std::move(prevNode));
732 
733  if (!nodeID.isRoot())
734  {
735  // we may have made this a node with 1 or 0 children
736  // And, if so, we need to remove this branch
737  const int bc = node->getBranchCount();
738  if (bc == 0)
739  {
740  // no children below this branch
741  prevNode.reset();
742  }
743  else if (bc == 1)
744  {
745  // If there's only one item, pull up on the thread
746  auto item = onlyBelow(node.get());
747 
748  if (item)
749  {
750  for (int i = 0; i < branchFactor; ++i)
751  {
752  if (!node->isEmptyBranch(i))
753  {
754  node->setChild(i, nullptr);
755  break;
756  }
757  }
758 
759  prevNode = makeTypedLeaf(type, item, node->cowid());
760  }
761  else
762  {
763  prevNode = std::move(node);
764  }
765  }
766  else
767  {
768  // This node is now the end of the branch
769  prevNode = std::move(node);
770  }
771  }
772  }
773 
774  return true;
775 }
776 
777 bool
779 {
780  assert(state_ != SHAMapState::Immutable);
781  assert(type != SHAMapNodeType::tnINNER);
782 
783  // add the specified item, does not update
784  uint256 tag = item->key();
785 
786  SharedPtrNodeStack stack;
787  walkTowardsKey(tag, &stack);
788 
789  if (stack.empty())
790  Throw<SHAMapMissingNode>(type_, tag);
791 
792  auto [node, nodeID] = stack.top();
793  stack.pop();
794 
795  if (node->isLeaf())
796  {
797  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
798  if (leaf->peekItem()->key() == tag)
799  return false;
800  }
801  node = unshareNode(std::move(node), nodeID);
802  if (node->isInner())
803  {
804  // easy case, we end on an inner node
805  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
806  int branch = selectBranch(nodeID, tag);
807  assert(inner->isEmptyBranch(branch));
808  inner->setChild(branch, makeTypedLeaf(type, std::move(item), cowid_));
809  }
810  else
811  {
812  // this is a leaf node that has to be made an inner node holding two
813  // items
814  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
815  std::shared_ptr<SHAMapItem const> otherItem = leaf->peekItem();
816  assert(otherItem && (tag != otherItem->key()));
817 
818  node = std::make_shared<SHAMapInnerNode>(node->cowid());
819 
820  unsigned int b1, b2;
821 
822  while ((b1 = selectBranch(nodeID, tag)) ==
823  (b2 = selectBranch(nodeID, otherItem->key())))
824  {
825  stack.push({node, nodeID});
826 
827  // we need a new inner node, since both go on same branch at this
828  // level
829  nodeID = nodeID.getChildNodeID(b1);
830  node = std::make_shared<SHAMapInnerNode>(cowid_);
831  }
832 
833  // we can add the two leaf nodes here
834  assert(node->isInner());
835 
836  auto inner = static_cast<SHAMapInnerNode*>(node.get());
837  inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
838  inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
839  }
840 
841  dirtyUp(stack, tag, node);
842  return true;
843 }
844 
845 bool
847 {
848  return addGiveItem(type, std::make_shared<SHAMapItem const>(std::move(i)));
849 }
850 
853 {
854  auto hash = root_->getHash();
855  if (hash.isZero())
856  {
857  const_cast<SHAMap&>(*this).unshare();
858  hash = root_->getHash();
859  }
860  return hash;
861 }
862 
863 bool
865  SHAMapNodeType type,
867 {
868  // can't change the tag but can change the hash
869  uint256 tag = item->key();
870 
871  assert(state_ != SHAMapState::Immutable);
872 
873  SharedPtrNodeStack stack;
874  walkTowardsKey(tag, &stack);
875 
876  if (stack.empty())
877  Throw<SHAMapMissingNode>(type_, tag);
878 
879  auto node = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
880  auto nodeID = stack.top().second;
881  stack.pop();
882 
883  if (!node || (node->peekItem()->key() != tag))
884  {
885  assert(false);
886  return false;
887  }
888 
889  if (node->getType() != type)
890  {
891  JLOG(journal_.fatal()) << "SHAMap::setItem: cross-type change!";
892  return false;
893  }
894 
895  node = unshareNode(std::move(node), nodeID);
896 
897  if (node->setItem(std::move(item)))
898  dirtyUp(stack, tag, node);
899 
900  return true;
901 }
902 
903 bool
905 {
906  if (hash == root_->getHash())
907  return true;
908 
909  if (auto stream = journal_.trace())
910  {
912  {
913  stream << "Fetch root TXN node " << hash;
914  }
915  else if (type_ == SHAMapType::STATE)
916  {
917  stream << "Fetch root STATE node " << hash;
918  }
919  else
920  {
921  stream << "Fetch root SHAMap node " << hash;
922  }
923  }
924 
925  auto newRoot = fetchNodeNT(hash, filter);
926 
927  if (newRoot)
928  {
929  root_ = newRoot;
930  assert(root_->getHash() == hash);
931  return true;
932  }
933 
934  return false;
935 }
936 
951 {
952  assert(node->cowid() == 0);
953  assert(backed_);
954 
955  canonicalize(node->getHash(), node);
956 
957  Serializer s;
958  node->serializeWithPrefix(s);
959  f_.db().store(
960  t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_);
961  return node;
962 }
963 
964 // We can't modify an inner node someone else might have a
965 // pointer to because flushing modifies inner nodes -- it
966 // makes them point to canonical/shared nodes.
967 template <class Node>
970 {
971  // A shared node should never need to be flushed
972  // because that would imply someone modified it
973  assert(node->cowid() != 0);
974 
975  if (node->cowid() != cowid_)
976  {
977  // Node is not uniquely ours, so unshare it before
978  // possibly modifying it
979  node = std::static_pointer_cast<Node>(node->clone(cowid_));
980  }
981  return node;
982 }
983 
984 int
986 {
987  // Don't share nodes with parent map
988  return walkSubTree(false, hotUNKNOWN);
989 }
990 
991 int
993 {
994  // We only write back if this map is backed.
995  return walkSubTree(backed_, t);
996 }
997 
998 int
1000 {
1001  assert(!doWrite || backed_);
1002 
1003  int flushed = 0;
1004 
1005  if (!root_ || (root_->cowid() == 0))
1006  return flushed;
1007 
1008  if (root_->isLeaf())
1009  { // special case -- root_ is leaf
1010  root_ = preFlushNode(std::move(root_));
1011  root_->updateHash();
1012  root_->unshare();
1013 
1014  if (doWrite)
1015  root_ = writeNode(t, std::move(root_));
1016 
1017  return 1;
1018  }
1019 
1020  auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
1021 
1022  if (node->isEmpty())
1023  { // replace empty root with a new empty root
1024  root_ = std::make_shared<SHAMapInnerNode>(0);
1025  return 1;
1026  }
1027 
1028  // Stack of {parent,index,child} pointers representing
1029  // inner nodes we are in the process of flushing
1030  using StackEntry = std::pair<std::shared_ptr<SHAMapInnerNode>, int>;
1032 
1033  node = preFlushNode(std::move(node));
1034 
1035  int pos = 0;
1036 
1037  // We can't flush an inner node until we flush its children
1038  while (1)
1039  {
1040  while (pos < branchFactor)
1041  {
1042  if (node->isEmptyBranch(pos))
1043  {
1044  ++pos;
1045  }
1046  else
1047  {
1048  // No need to do I/O. If the node isn't linked,
1049  // it can't need to be flushed
1050  int branch = pos;
1051  auto child = node->getChild(pos++);
1052 
1053  if (child && (child->cowid() != 0))
1054  {
1055  // This is a node that needs to be flushed
1056 
1057  child = preFlushNode(std::move(child));
1058 
1059  if (child->isInner())
1060  {
1061  // save our place and work on this node
1062 
1063  stack.emplace(std::move(node), branch);
1064  // The semantics of this changes when we move to c++-20
1065  // Right now no move will occur; With c++-20 child will
1066  // be moved from.
1067  node = std::static_pointer_cast<SHAMapInnerNode>(
1068  std::move(child));
1069  pos = 0;
1070  }
1071  else
1072  {
1073  // flush this leaf
1074  ++flushed;
1075 
1076  assert(node->cowid() == cowid_);
1077  child->updateHash();
1078  child->unshare();
1079 
1080  if (doWrite)
1081  child = writeNode(t, std::move(child));
1082 
1083  node->shareChild(branch, child);
1084  }
1085  }
1086  }
1087  }
1088 
1089  // update the hash of this inner node
1090  node->updateHashDeep();
1091 
1092  // This inner node can now be shared
1093  node->unshare();
1094 
1095  if (doWrite)
1096  node = std::static_pointer_cast<SHAMapInnerNode>(
1097  writeNode(t, std::move(node)));
1098 
1099  ++flushed;
1100 
1101  if (stack.empty())
1102  break;
1103 
1104  auto parent = std::move(stack.top().first);
1105  pos = stack.top().second;
1106  stack.pop();
1107 
1108  // Hook this inner node to its parent
1109  assert(parent->cowid() == cowid_);
1110  parent->shareChild(pos, node);
1111 
1112  // Continue with parent's next child, if any
1113  node = std::move(parent);
1114  ++pos;
1115  }
1116 
1117  // Last inner node is the new root_
1118  root_ = std::move(node);
1119 
1120  return flushed;
1121 }
1122 
1123 void
1124 SHAMap::dump(bool hash) const
1125 {
1126  int leafCount = 0;
1127  JLOG(journal_.info()) << " MAP Contains";
1128 
1130  stack.push({root_.get(), SHAMapNodeID()});
1131 
1132  do
1133  {
1134  auto [node, nodeID] = stack.top();
1135  stack.pop();
1136 
1137  JLOG(journal_.info()) << node->getString(nodeID);
1138  if (hash)
1139  {
1140  JLOG(journal_.info()) << "Hash: " << node->getHash();
1141  }
1142 
1143  if (node->isInner())
1144  {
1145  auto inner = static_cast<SHAMapInnerNode*>(node);
1146  for (int i = 0; i < branchFactor; ++i)
1147  {
1148  if (!inner->isEmptyBranch(i))
1149  {
1150  auto child = inner->getChildPointer(i);
1151  if (child)
1152  {
1153  assert(child->getHash() == inner->getChildHash(i));
1154  stack.push({child, nodeID.getChildNodeID(i)});
1155  }
1156  }
1157  }
1158  }
1159  else
1160  ++leafCount;
1161  } while (!stack.empty());
1162 
1163  JLOG(journal_.info()) << leafCount << " resident leaves";
1164 }
1165 
1168 {
1169  auto ret = f_.getTreeNodeCache(ledgerSeq_)->fetch(hash.as_uint256());
1170  assert(!ret || !ret->cowid());
1171  return ret;
1172 }
1173 
1174 void
1176  SHAMapHash const& hash,
1177  std::shared_ptr<SHAMapTreeNode>& node) const
1178 {
1179  assert(backed_);
1180  assert(node->cowid() == 0);
1181  assert(node->getHash() == hash);
1182 
1184  ->canonicalize_replace_client(hash.as_uint256(), node);
1185 }
1186 
1187 void
1189 {
1190  (void)getHash(); // update node hashes
1191  auto node = root_.get();
1192  assert(node != nullptr);
1193  assert(!node->isLeaf());
1194  SharedPtrNodeStack stack;
1195  for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1196  leaf = peekNextItem(leaf->peekItem()->key(), stack))
1197  ;
1198  node->invariants(true);
1199 }
1200 
1201 } // 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:1188
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:350
ripple::ListDisposition::pending
@ pending
List will be valid in the future.
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:567
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:852
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:846
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:999
ripple::SHAMap::dump
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1124
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:517
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:596
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:282
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:270
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:450
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:1167
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
ripple::SHAMapMissingNode
Definition: SHAMapMissingNode.h:55
std::underlying_type_t
ripple::SHAMap::lastBelow
SHAMapLeafNode * lastBelow(std::shared_ptr< SHAMapTreeNode > node, SharedPtrNodeStack &stack, int branch=branchFactor) const
Definition: SHAMap.cpp:491
ripple::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:198
ripple::Family::missingNodeAcquireBySeq
virtual void missingNodeAcquireBySeq(std::uint32_t refNum, uint256 const &nodeHash)=0
Acquire ledger that has a missing node by ledger sequence.
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:694
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:361
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > child)
Definition: SHAMapInnerNode.cpp:287
ripple::SHAMapInnerNode
Definition: SHAMapInnerNode.h:41
ripple::SHAMap::peekFirstItem
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
Definition: SHAMap.cpp:553
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:514
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:503
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:293
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:1175
ripple::Family
Definition: Family.h:32
std::runtime_error
STL class.
std::uint32_t
ripple::SHAMap::fetchRoot
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
Definition: SHAMap.cpp:904
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:619
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:214
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:864
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:778
ripple::SHAMap::unshare
int unshare()
Convert any modified nodes to shared.
Definition: SHAMap.cpp:985
ripple::SHAMap::descendAsync
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
Definition: SHAMap.cpp:390
ripple::SHAMap::writeNode
std::shared_ptr< SHAMapTreeNode > writeNode(NodeObjectType t, std::shared_ptr< SHAMapTreeNode > node) const
write and canonicalize modified node
Definition: SHAMap.cpp:950
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:969
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:992
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:656
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:434
ripple::SHAMap::descend
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:316
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:700
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::runtime_error::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