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