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,
33  boost::intrusive_ptr<SHAMapItem const> item,
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 
69 SHAMap::SHAMap(SHAMap const& other, bool isMutable)
70  : f_(other.f_)
71  , journal_(other.f_.journal())
72  , cowid_(other.cowid_ + 1)
73  , ledgerSeq_(other.ledgerSeq_)
74  , root_(other.root_)
75  , state_(isMutable ? SHAMapState::Modifying : SHAMapState::Immutable)
76  , type_(other.type_)
77  , backed_(other.backed_)
78 {
79  // If either map may change, they cannot share nodes
80  if ((state_ != SHAMapState::Immutable) ||
81  (other.state_ != SHAMapState::Immutable))
82  {
83  unshare();
84  }
85 }
86 
88 SHAMap::snapShot(bool isMutable) const
89 {
90  return std::make_shared<SHAMap>(*this, isMutable);
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 
177  try
178  {
179  if (!object)
180  {
181  if (full_)
182  {
183  full_ = false;
185  }
186  return {};
187  }
188 
189  auto node =
190  SHAMapTreeNode::makeFromPrefix(makeSlice(object->getData()), hash);
191  if (node)
192  canonicalize(hash, node);
193  return node;
194  }
195  catch (std::runtime_error const& e)
196  {
197  JLOG(journal_.warn()) << "finishFetch exception: " << e.what();
198  }
199  catch (...)
200  {
201  JLOG(journal_.warn())
202  << "finishFetch exception: unknonw exception: " << hash;
203  }
204 
205  return {};
206 }
207 
208 // See if a sync filter has a node
211 {
212  if (auto nodeData = filter->getNode(hash))
213  {
214  try
215  {
216  auto node =
217  SHAMapTreeNode::makeFromPrefix(makeSlice(*nodeData), hash);
218  if (node)
219  {
220  filter->gotNode(
221  true,
222  hash,
223  ledgerSeq_,
224  std::move(*nodeData),
225  node->getType());
226  if (backed_)
227  canonicalize(hash, node);
228  }
229  return node;
230  }
231  catch (std::exception const& x)
232  {
233  JLOG(f_.journal().warn())
234  << "Invalid node/data, hash=" << hash << ": " << x.what();
235  }
236  }
237  return {};
238 }
239 
240 // Get a node without throwing
241 // Used on maps where missing nodes are expected
244 {
245  auto node = cacheLookup(hash);
246  if (node)
247  return node;
248 
249  if (backed_)
250  {
251  node = fetchNodeFromDB(hash);
252  if (node)
253  {
254  canonicalize(hash, node);
255  return node;
256  }
257  }
258 
259  if (filter)
260  node = checkFilter(hash, filter);
261 
262  return node;
263 }
264 
266 SHAMap::fetchNodeNT(SHAMapHash const& hash) const
267 {
268  auto node = cacheLookup(hash);
269 
270  if (!node && backed_)
271  node = fetchNodeFromDB(hash);
272 
273  return node;
274 }
275 
276 // Throw if the node is missing
278 SHAMap::fetchNode(SHAMapHash const& hash) const
279 {
280  auto node = fetchNodeNT(hash);
281 
282  if (!node)
283  Throw<SHAMapMissingNode>(type_, hash);
284 
285  return node;
286 }
287 
289 SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const
290 {
291  SHAMapTreeNode* ret = descend(parent, branch);
292 
293  if (!ret && !parent->isEmptyBranch(branch))
294  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
295 
296  return ret;
297 }
298 
301  const
302 {
303  std::shared_ptr<SHAMapTreeNode> ret = descend(parent, branch);
304 
305  if (!ret && !parent->isEmptyBranch(branch))
306  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
307 
308  return ret;
309 }
310 
312 SHAMap::descend(SHAMapInnerNode* parent, int branch) const
313 {
314  SHAMapTreeNode* ret = parent->getChildPointer(branch);
315  if (ret || !backed_)
316  return ret;
317 
319  fetchNodeNT(parent->getChildHash(branch));
320  if (!node)
321  return nullptr;
322 
323  node = parent->canonicalizeChild(branch, std::move(node));
324  return node.get();
325 }
326 
329  const
330 {
331  std::shared_ptr<SHAMapTreeNode> node = parent->getChild(branch);
332  if (node || !backed_)
333  return node;
334 
335  node = fetchNode(parent->getChildHash(branch));
336  if (!node)
337  return nullptr;
338 
339  node = parent->canonicalizeChild(branch, std::move(node));
340  return node;
341 }
342 
343 // Gets the node that would be hooked to this branch,
344 // but doesn't hook it up.
347  std::shared_ptr<SHAMapInnerNode> const& parent,
348  int branch) const
349 {
350  std::shared_ptr<SHAMapTreeNode> ret = parent->getChild(branch);
351  if (!ret && backed_)
352  ret = fetchNode(parent->getChildHash(branch));
353  return ret;
354 }
355 
358  SHAMapInnerNode* parent,
359  SHAMapNodeID const& parentID,
360  int branch,
361  SHAMapSyncFilter* filter) const
362 {
363  assert(parent->isInner());
364  assert((branch >= 0) && (branch < branchFactor));
365  assert(!parent->isEmptyBranch(branch));
366 
367  SHAMapTreeNode* child = parent->getChildPointer(branch);
368 
369  if (!child)
370  {
371  auto const& childHash = parent->getChildHash(branch);
373  fetchNodeNT(childHash, filter);
374 
375  if (childNode)
376  {
377  childNode = parent->canonicalizeChild(branch, std::move(childNode));
378  child = childNode.get();
379  }
380  }
381 
382  return std::make_pair(child, parentID.getChildNodeID(branch));
383 }
384 
387  SHAMapInnerNode* parent,
388  int branch,
389  SHAMapSyncFilter* filter,
390  bool& pending,
391  descendCallback&& callback) const
392 {
393  pending = false;
394 
395  SHAMapTreeNode* ret = parent->getChildPointer(branch);
396  if (ret)
397  return ret;
398 
399  auto const& hash = parent->getChildHash(branch);
400 
401  auto ptr = cacheLookup(hash);
402  if (!ptr)
403  {
404  if (filter)
405  ptr = checkFilter(hash, filter);
406 
407  if (!ptr && backed_)
408  {
409  f_.db().asyncFetch(
410  hash.as_uint256(),
411  ledgerSeq_,
412  [this, hash, cb{std::move(callback)}](
413  std::shared_ptr<NodeObject> const& object) {
414  auto node = finishFetch(hash, object);
415  cb(node, hash);
416  });
417  pending = true;
418  return nullptr;
419  }
420  }
421 
422  if (ptr)
423  ptr = parent->canonicalizeChild(branch, std::move(ptr));
424 
425  return ptr.get();
426 }
427 
428 template <class Node>
431 {
432  // make sure the node is suitable for the intended operation (copy on write)
433  assert(node->cowid() <= cowid_);
434  if (node->cowid() != cowid_)
435  {
436  // have a CoW
437  assert(state_ != SHAMapState::Immutable);
438  node = std::static_pointer_cast<Node>(node->clone(cowid_));
439  if (nodeID.isRoot())
440  root_ = node;
441  }
442  return node;
443 }
444 
448  SharedPtrNodeStack& stack,
449  int branch,
450  std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const&
451  loopParams) const
452 {
453  auto& [init, cmp, incr] = loopParams;
454  if (node->isLeaf())
455  {
456  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
457  stack.push({node, {leafDepth, n->peekItem()->key()}});
458  return n.get();
459  }
460  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
461  if (stack.empty())
462  stack.push({inner, SHAMapNodeID{}});
463  else
464  stack.push({inner, stack.top().second.getChildNodeID(branch)});
465  for (int i = init; cmp(i);)
466  {
467  if (!inner->isEmptyBranch(i))
468  {
469  node = descendThrow(inner, i);
470  assert(!stack.empty());
471  if (node->isLeaf())
472  {
473  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
474  stack.push({n, {leafDepth, n->peekItem()->key()}});
475  return n.get();
476  }
477  inner = std::static_pointer_cast<SHAMapInnerNode>(node);
478  stack.push({inner, stack.top().second.getChildNodeID(branch)});
479  i = init; // descend and reset loop
480  }
481  else
482  incr(i); // scan next branch
483  }
484  return nullptr;
485 }
489  SharedPtrNodeStack& stack,
490  int branch) const
491 {
492  auto init = branchFactor - 1;
493  auto cmp = [](int i) { return i >= 0; };
494  auto incr = [](int& i) { --i; };
495 
496  return belowHelper(node, stack, branch, {init, cmp, incr});
497 }
501  SharedPtrNodeStack& stack,
502  int branch) const
503 {
504  auto init = 0;
505  auto cmp = [](int i) { return i <= branchFactor; };
506  auto incr = [](int& i) { ++i; };
507 
508  return belowHelper(node, stack, branch, {init, cmp, incr});
509 }
510 static const boost::intrusive_ptr<SHAMapItem const> no_item;
511 
512 boost::intrusive_ptr<SHAMapItem const> const&
514 {
515  // If there is only one item below this node, return it
516 
517  while (!node->isLeaf())
518  {
519  SHAMapTreeNode* nextNode = nullptr;
520  auto inner = static_cast<SHAMapInnerNode*>(node);
521  for (int i = 0; i < branchFactor; ++i)
522  {
523  if (!inner->isEmptyBranch(i))
524  {
525  if (nextNode)
526  return no_item;
527 
528  nextNode = descendThrow(inner, i);
529  }
530  }
531 
532  if (!nextNode)
533  {
534  assert(false);
535  return no_item;
536  }
537 
538  node = nextNode;
539  }
540 
541  // An inner node must have at least one leaf
542  // below it, unless it's the root_
543  auto const leaf = static_cast<SHAMapLeafNode const*>(node);
544  assert(leaf->peekItem() || (leaf == root_.get()));
545  return leaf->peekItem();
546 }
547 
548 SHAMapLeafNode const*
550 {
551  assert(stack.empty());
552  SHAMapLeafNode* node = firstBelow(root_, stack);
553  if (!node)
554  {
555  while (!stack.empty())
556  stack.pop();
557  return nullptr;
558  }
559  return node;
560 }
561 
562 SHAMapLeafNode const*
564 {
565  assert(!stack.empty());
566  assert(stack.top().first->isLeaf());
567  stack.pop();
568  while (!stack.empty())
569  {
570  auto [node, nodeID] = stack.top();
571  assert(!node->isLeaf());
572  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
573  for (auto i = selectBranch(nodeID, id) + 1; i < branchFactor; ++i)
574  {
575  if (!inner->isEmptyBranch(i))
576  {
577  node = descendThrow(inner, i);
578  auto leaf = firstBelow(node, stack, i);
579  if (!leaf)
580  Throw<SHAMapMissingNode>(type_, id);
581  assert(leaf->isLeaf());
582  return leaf;
583  }
584  }
585  stack.pop();
586  }
587  // must be last item
588  return nullptr;
589 }
590 
591 boost::intrusive_ptr<SHAMapItem const> const&
592 SHAMap::peekItem(uint256 const& id) const
593 {
594  SHAMapLeafNode* leaf = findKey(id);
595 
596  if (!leaf)
597  return no_item;
598 
599  return leaf->peekItem();
600 }
601 
602 boost::intrusive_ptr<SHAMapItem const> const&
603 SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const
604 {
605  SHAMapLeafNode* leaf = findKey(id);
606 
607  if (!leaf)
608  return no_item;
609 
610  hash = leaf->getHash();
611  return leaf->peekItem();
612 }
613 
615 SHAMap::upper_bound(uint256 const& id) const
616 {
617  SharedPtrNodeStack stack;
618  walkTowardsKey(id, &stack);
619  while (!stack.empty())
620  {
621  auto [node, nodeID] = stack.top();
622  if (node->isLeaf())
623  {
624  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
625  if (leaf->peekItem()->key() > id)
626  return const_iterator(
627  this, leaf->peekItem().get(), std::move(stack));
628  }
629  else
630  {
631  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
632  for (auto branch = selectBranch(nodeID, id) + 1;
633  branch < branchFactor;
634  ++branch)
635  {
636  if (!inner->isEmptyBranch(branch))
637  {
638  node = descendThrow(inner, branch);
639  auto leaf = firstBelow(node, stack, branch);
640  if (!leaf)
641  Throw<SHAMapMissingNode>(type_, id);
642  return const_iterator(
643  this, leaf->peekItem().get(), std::move(stack));
644  }
645  }
646  }
647  stack.pop();
648  }
649  return end();
650 }
652 SHAMap::lower_bound(uint256 const& id) const
653 {
654  SharedPtrNodeStack stack;
655  walkTowardsKey(id, &stack);
656  while (!stack.empty())
657  {
658  auto [node, nodeID] = stack.top();
659  if (node->isLeaf())
660  {
661  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
662  if (leaf->peekItem()->key() < id)
663  return const_iterator(
664  this, leaf->peekItem().get(), std::move(stack));
665  }
666  else
667  {
668  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
669  for (int branch = selectBranch(nodeID, id) - 1; branch >= 0;
670  --branch)
671  {
672  if (!inner->isEmptyBranch(branch))
673  {
674  node = descendThrow(inner, branch);
675  auto leaf = lastBelow(node, stack, branch);
676  if (!leaf)
677  Throw<SHAMapMissingNode>(type_, id);
678  return const_iterator(
679  this, leaf->peekItem().get(), std::move(stack));
680  }
681  }
682  }
683  stack.pop();
684  }
685  // TODO: what to return here?
686  return end();
687 }
688 
689 bool
690 SHAMap::hasItem(uint256 const& id) const
691 {
692  return (findKey(id) != nullptr);
693 }
694 
695 bool
697 {
698  // delete the item with this ID
699  assert(state_ != SHAMapState::Immutable);
700 
701  SharedPtrNodeStack stack;
702  walkTowardsKey(id, &stack);
703 
704  if (stack.empty())
705  Throw<SHAMapMissingNode>(type_, id);
706 
707  auto leaf = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
708  stack.pop();
709 
710  if (!leaf || (leaf->peekItem()->key() != id))
711  return false;
712 
713  SHAMapNodeType type = leaf->getType();
714 
715  // What gets attached to the end of the chain
716  // (For now, nothing, since we deleted the leaf)
718 
719  while (!stack.empty())
720  {
721  auto node =
722  std::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
723  SHAMapNodeID nodeID = stack.top().second;
724  stack.pop();
725 
726  node = unshareNode(std::move(node), nodeID);
727  node->setChild(selectBranch(nodeID, id), std::move(prevNode));
728 
729  if (!nodeID.isRoot())
730  {
731  // we may have made this a node with 1 or 0 children
732  // And, if so, we need to remove this branch
733  const int bc = node->getBranchCount();
734  if (bc == 0)
735  {
736  // no children below this branch
737  prevNode.reset();
738  }
739  else if (bc == 1)
740  {
741  // If there's only one item, pull up on the thread
742  auto item = onlyBelow(node.get());
743 
744  if (item)
745  {
746  for (int i = 0; i < branchFactor; ++i)
747  {
748  if (!node->isEmptyBranch(i))
749  {
750  node->setChild(i, nullptr);
751  break;
752  }
753  }
754 
755  prevNode = makeTypedLeaf(type, item, node->cowid());
756  }
757  else
758  {
759  prevNode = std::move(node);
760  }
761  }
762  else
763  {
764  // This node is now the end of the branch
765  prevNode = std::move(node);
766  }
767  }
768  }
769 
770  return true;
771 }
772 
773 bool
775  SHAMapNodeType type,
776  boost::intrusive_ptr<SHAMapItem const> item)
777 {
778  assert(state_ != SHAMapState::Immutable);
779  assert(type != SHAMapNodeType::tnINNER);
780 
781  // add the specified item, does not update
782  uint256 tag = item->key();
783 
784  SharedPtrNodeStack stack;
785  walkTowardsKey(tag, &stack);
786 
787  if (stack.empty())
788  Throw<SHAMapMissingNode>(type_, tag);
789 
790  auto [node, nodeID] = stack.top();
791  stack.pop();
792 
793  if (node->isLeaf())
794  {
795  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
796  if (leaf->peekItem()->key() == tag)
797  return false;
798  }
799  node = unshareNode(std::move(node), nodeID);
800  if (node->isInner())
801  {
802  // easy case, we end on an inner node
803  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
804  int branch = selectBranch(nodeID, tag);
805  assert(inner->isEmptyBranch(branch));
806  inner->setChild(branch, makeTypedLeaf(type, std::move(item), cowid_));
807  }
808  else
809  {
810  // this is a leaf node that has to be made an inner node holding two
811  // items
812  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
813  auto otherItem = leaf->peekItem();
814  assert(otherItem && (tag != otherItem->key()));
815 
816  node = std::make_shared<SHAMapInnerNode>(node->cowid());
817 
818  unsigned int b1, b2;
819 
820  while ((b1 = selectBranch(nodeID, tag)) ==
821  (b2 = selectBranch(nodeID, otherItem->key())))
822  {
823  stack.push({node, nodeID});
824 
825  // we need a new inner node, since both go on same branch at this
826  // level
827  nodeID = nodeID.getChildNodeID(b1);
828  node = std::make_shared<SHAMapInnerNode>(cowid_);
829  }
830 
831  // we can add the two leaf nodes here
832  assert(node->isInner());
833 
834  auto inner = static_cast<SHAMapInnerNode*>(node.get());
835  inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
836  inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
837  }
838 
839  dirtyUp(stack, tag, node);
840  return true;
841 }
842 
843 bool
845  SHAMapNodeType type,
846  boost::intrusive_ptr<SHAMapItem const> item)
847 {
848  return addGiveItem(type, std::move(item));
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,
866  boost::intrusive_ptr<SHAMapItem const> item)
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::updateGiveItem: cross-type change!";
892  return false;
893  }
894 
895  node = unshareNode(std::move(node), nodeID);
896 
897  if (node->setItem(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::makeTypedLeaf
std::shared_ptr< SHAMapLeafNode > makeTypedLeaf(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item, std::uint32_t owner)
Definition: SHAMap.cpp:31
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:346
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::peekNextItem
SHAMapLeafNode const * peekNextItem(uint256 const &id, SharedPtrNodeStack &stack) const
Definition: SHAMap.cpp:563
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
ripple::SHAMap::updateGiveItem
bool updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition: SHAMap.cpp:864
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::no_item
static const boost::intrusive_ptr< SHAMapItem const > no_item
Definition: SHAMap.cpp:510
ripple::SHAMap::peekItem
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition: SHAMap.cpp:592
ripple::SHAMapLeafNode::peekItem
boost::intrusive_ptr< SHAMapItem const > const & peekItem() const
Definition: SHAMapLeafNode.cpp:44
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::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::SHAMapNodeType
SHAMapNodeType
Definition: SHAMapTreeNode.h:46
std::stack< std::pair< std::shared_ptr< SHAMapTreeNode >, SHAMapNodeID > >
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:278
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:266
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:446
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:88
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
ripple::SHAMap::addItem
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition: SHAMap.cpp:844
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:487
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:690
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:549
ripple::SHAMap::addGiveItem
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition: SHAMap.cpp:774
ripple::SHAMap::onlyBelow
boost::intrusive_ptr< SHAMapItem const > const & onlyBelow(SHAMapTreeNode *) const
If there is only one leaf below this node, get its contents.
Definition: SHAMap.cpp:513
ripple::SHAMapSyncFilter::gotNode
virtual void gotNode(bool fromFilter, SHAMapHash const &nodeHash, std::uint32_t ledgerSeq, Blob &&nodeData, SHAMapNodeType type) const =0
ripple::SHAMap::const_iterator
Definition: SHAMap.h:638
ripple::SHAMap::firstBelow
SHAMapLeafNode * firstBelow(std::shared_ptr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) const
Definition: SHAMap.cpp:499
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:289
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:615
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:210
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::end
const_iterator end() const
Definition: SHAMap.h:752
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::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:386
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:652
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:430
ripple::SHAMap::descend
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:312
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::SHAMap
SHAMap()=delete
ripple::SHAMap::delItem
bool delItem(uint256 const &id)
Definition: SHAMap.cpp:696
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