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 
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 }
515 static const boost::intrusive_ptr<SHAMapItem const> no_item;
516 
517 boost::intrusive_ptr<SHAMapItem const> const&
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 
596 boost::intrusive_ptr<SHAMapItem const> const&
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 
607 boost::intrusive_ptr<SHAMapItem const> const&
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  SHAMapNodeType type,
781  boost::intrusive_ptr<SHAMapItem const> item)
782 {
783  assert(state_ != SHAMapState::Immutable);
784  assert(type != SHAMapNodeType::tnINNER);
785 
786  // add the specified item, does not update
787  uint256 tag = item->key();
788 
789  SharedPtrNodeStack stack;
790  walkTowardsKey(tag, &stack);
791 
792  if (stack.empty())
793  Throw<SHAMapMissingNode>(type_, tag);
794 
795  auto [node, nodeID] = stack.top();
796  stack.pop();
797 
798  if (node->isLeaf())
799  {
800  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
801  if (leaf->peekItem()->key() == tag)
802  return false;
803  }
804  node = unshareNode(std::move(node), nodeID);
805  if (node->isInner())
806  {
807  // easy case, we end on an inner node
808  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
809  int branch = selectBranch(nodeID, tag);
810  assert(inner->isEmptyBranch(branch));
811  inner->setChild(branch, makeTypedLeaf(type, std::move(item), cowid_));
812  }
813  else
814  {
815  // this is a leaf node that has to be made an inner node holding two
816  // items
817  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
818  auto otherItem = leaf->peekItem();
819  assert(otherItem && (tag != otherItem->key()));
820 
821  node = std::make_shared<SHAMapInnerNode>(node->cowid());
822 
823  unsigned int b1, b2;
824 
825  while ((b1 = selectBranch(nodeID, tag)) ==
826  (b2 = selectBranch(nodeID, otherItem->key())))
827  {
828  stack.push({node, nodeID});
829 
830  // we need a new inner node, since both go on same branch at this
831  // level
832  nodeID = nodeID.getChildNodeID(b1);
833  node = std::make_shared<SHAMapInnerNode>(cowid_);
834  }
835 
836  // we can add the two leaf nodes here
837  assert(node->isInner());
838 
839  auto inner = static_cast<SHAMapInnerNode*>(node.get());
840  inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
841  inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
842  }
843 
844  dirtyUp(stack, tag, node);
845  return true;
846 }
847 
848 bool
850  SHAMapNodeType type,
851  boost::intrusive_ptr<SHAMapItem const> item)
852 {
853  return addGiveItem(type, std::move(item));
854 }
855 
858 {
859  auto hash = root_->getHash();
860  if (hash.isZero())
861  {
862  const_cast<SHAMap&>(*this).unshare();
863  hash = root_->getHash();
864  }
865  return hash;
866 }
867 
868 bool
870  SHAMapNodeType type,
871  boost::intrusive_ptr<SHAMapItem const> item)
872 {
873  // can't change the tag but can change the hash
874  uint256 tag = item->key();
875 
876  assert(state_ != SHAMapState::Immutable);
877 
878  SharedPtrNodeStack stack;
879  walkTowardsKey(tag, &stack);
880 
881  if (stack.empty())
882  Throw<SHAMapMissingNode>(type_, tag);
883 
884  auto node = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
885  auto nodeID = stack.top().second;
886  stack.pop();
887 
888  if (!node || (node->peekItem()->key() != tag))
889  {
890  assert(false);
891  return false;
892  }
893 
894  if (node->getType() != type)
895  {
896  JLOG(journal_.fatal()) << "SHAMap::updateGiveItem: cross-type change!";
897  return false;
898  }
899 
900  node = unshareNode(std::move(node), nodeID);
901 
902  if (node->setItem(item))
903  dirtyUp(stack, tag, node);
904 
905  return true;
906 }
907 
908 bool
910 {
911  if (hash == root_->getHash())
912  return true;
913 
914  if (auto stream = journal_.trace())
915  {
917  {
918  stream << "Fetch root TXN node " << hash;
919  }
920  else if (type_ == SHAMapType::STATE)
921  {
922  stream << "Fetch root STATE node " << hash;
923  }
924  else
925  {
926  stream << "Fetch root SHAMap node " << hash;
927  }
928  }
929 
930  auto newRoot = fetchNodeNT(hash, filter);
931 
932  if (newRoot)
933  {
934  root_ = newRoot;
935  assert(root_->getHash() == hash);
936  return true;
937  }
938 
939  return false;
940 }
941 
956 {
957  assert(node->cowid() == 0);
958  assert(backed_);
959 
960  canonicalize(node->getHash(), node);
961 
962  Serializer s;
963  node->serializeWithPrefix(s);
964  f_.db().store(
965  t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_);
966  return node;
967 }
968 
969 // We can't modify an inner node someone else might have a
970 // pointer to because flushing modifies inner nodes -- it
971 // makes them point to canonical/shared nodes.
972 template <class Node>
975 {
976  // A shared node should never need to be flushed
977  // because that would imply someone modified it
978  assert(node->cowid() != 0);
979 
980  if (node->cowid() != cowid_)
981  {
982  // Node is not uniquely ours, so unshare it before
983  // possibly modifying it
984  node = std::static_pointer_cast<Node>(node->clone(cowid_));
985  }
986  return node;
987 }
988 
989 int
991 {
992  // Don't share nodes with parent map
993  return walkSubTree(false, hotUNKNOWN);
994 }
995 
996 int
998 {
999  // We only write back if this map is backed.
1000  return walkSubTree(backed_, t);
1001 }
1002 
1003 int
1005 {
1006  assert(!doWrite || backed_);
1007 
1008  int flushed = 0;
1009 
1010  if (!root_ || (root_->cowid() == 0))
1011  return flushed;
1012 
1013  if (root_->isLeaf())
1014  { // special case -- root_ is leaf
1015  root_ = preFlushNode(std::move(root_));
1016  root_->updateHash();
1017  root_->unshare();
1018 
1019  if (doWrite)
1020  root_ = writeNode(t, std::move(root_));
1021 
1022  return 1;
1023  }
1024 
1025  auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
1026 
1027  if (node->isEmpty())
1028  { // replace empty root with a new empty root
1029  root_ = std::make_shared<SHAMapInnerNode>(0);
1030  return 1;
1031  }
1032 
1033  // Stack of {parent,index,child} pointers representing
1034  // inner nodes we are in the process of flushing
1035  using StackEntry = std::pair<std::shared_ptr<SHAMapInnerNode>, int>;
1037 
1038  node = preFlushNode(std::move(node));
1039 
1040  int pos = 0;
1041 
1042  // We can't flush an inner node until we flush its children
1043  while (1)
1044  {
1045  while (pos < branchFactor)
1046  {
1047  if (node->isEmptyBranch(pos))
1048  {
1049  ++pos;
1050  }
1051  else
1052  {
1053  // No need to do I/O. If the node isn't linked,
1054  // it can't need to be flushed
1055  int branch = pos;
1056  auto child = node->getChild(pos++);
1057 
1058  if (child && (child->cowid() != 0))
1059  {
1060  // This is a node that needs to be flushed
1061 
1062  child = preFlushNode(std::move(child));
1063 
1064  if (child->isInner())
1065  {
1066  // save our place and work on this node
1067 
1068  stack.emplace(std::move(node), branch);
1069  // The semantics of this changes when we move to c++-20
1070  // Right now no move will occur; With c++-20 child will
1071  // be moved from.
1072  node = std::static_pointer_cast<SHAMapInnerNode>(
1073  std::move(child));
1074  pos = 0;
1075  }
1076  else
1077  {
1078  // flush this leaf
1079  ++flushed;
1080 
1081  assert(node->cowid() == cowid_);
1082  child->updateHash();
1083  child->unshare();
1084 
1085  if (doWrite)
1086  child = writeNode(t, std::move(child));
1087 
1088  node->shareChild(branch, child);
1089  }
1090  }
1091  }
1092  }
1093 
1094  // update the hash of this inner node
1095  node->updateHashDeep();
1096 
1097  // This inner node can now be shared
1098  node->unshare();
1099 
1100  if (doWrite)
1101  node = std::static_pointer_cast<SHAMapInnerNode>(
1102  writeNode(t, std::move(node)));
1103 
1104  ++flushed;
1105 
1106  if (stack.empty())
1107  break;
1108 
1109  auto parent = std::move(stack.top().first);
1110  pos = stack.top().second;
1111  stack.pop();
1112 
1113  // Hook this inner node to its parent
1114  assert(parent->cowid() == cowid_);
1115  parent->shareChild(pos, node);
1116 
1117  // Continue with parent's next child, if any
1118  node = std::move(parent);
1119  ++pos;
1120  }
1121 
1122  // Last inner node is the new root_
1123  root_ = std::move(node);
1124 
1125  return flushed;
1126 }
1127 
1128 void
1129 SHAMap::dump(bool hash) const
1130 {
1131  int leafCount = 0;
1132  JLOG(journal_.info()) << " MAP Contains";
1133 
1135  stack.push({root_.get(), SHAMapNodeID()});
1136 
1137  do
1138  {
1139  auto [node, nodeID] = stack.top();
1140  stack.pop();
1141 
1142  JLOG(journal_.info()) << node->getString(nodeID);
1143  if (hash)
1144  {
1145  JLOG(journal_.info()) << "Hash: " << node->getHash();
1146  }
1147 
1148  if (node->isInner())
1149  {
1150  auto inner = static_cast<SHAMapInnerNode*>(node);
1151  for (int i = 0; i < branchFactor; ++i)
1152  {
1153  if (!inner->isEmptyBranch(i))
1154  {
1155  auto child = inner->getChildPointer(i);
1156  if (child)
1157  {
1158  assert(child->getHash() == inner->getChildHash(i));
1159  stack.push({child, nodeID.getChildNodeID(i)});
1160  }
1161  }
1162  }
1163  }
1164  else
1165  ++leafCount;
1166  } while (!stack.empty());
1167 
1168  JLOG(journal_.info()) << leafCount << " resident leaves";
1169 }
1170 
1173 {
1174  auto ret = f_.getTreeNodeCache(ledgerSeq_)->fetch(hash.as_uint256());
1175  assert(!ret || !ret->cowid());
1176  return ret;
1177 }
1178 
1179 void
1181  SHAMapHash const& hash,
1182  std::shared_ptr<SHAMapTreeNode>& node) const
1183 {
1184  assert(backed_);
1185  assert(node->cowid() == 0);
1186  assert(node->getHash() == hash);
1187 
1189  ->canonicalize_replace_client(hash.as_uint256(), node);
1190 }
1191 
1192 void
1194 {
1195  (void)getHash(); // update node hashes
1196  auto node = root_.get();
1197  assert(node != nullptr);
1198  assert(!node->isLeaf());
1199  SharedPtrNodeStack stack;
1200  for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1201  leaf = peekNextItem(leaf->peekItem()->key(), stack))
1202  ;
1203  node->invariants(true);
1204 }
1205 
1206 } // 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:1193
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:857
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:869
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:515
ripple::SHAMap::peekItem
boost::intrusive_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition: SHAMap.cpp:597
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:1004
ripple::SHAMap::dump
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1129
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: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:1172
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:849
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::SHAMap::addGiveItem
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition: SHAMap.cpp:779
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:518
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:634
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:1180
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:909
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::end
const_iterator end() const
Definition: SHAMap.h:748
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:990
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:955
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:974
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:997
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::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