rippled
SHAMap.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/basics/contract.h>
21 #include <ripple/shamap/SHAMap.h>
22 #include <ripple/shamap/SHAMapAccountStateLeafNode.h>
23 #include <ripple/shamap/SHAMapNodeID.h>
24 #include <ripple/shamap/SHAMapSyncFilter.h>
25 #include <ripple/shamap/SHAMapTxLeafNode.h>
26 #include <ripple/shamap/SHAMapTxPlusMetaLeafNode.h>
27 
28 namespace ripple {
29 
32  SHAMapNodeType type,
34  std::uint32_t owner)
35 {
37  return std::make_shared<SHAMapTxLeafNode>(std::move(item), owner);
38 
40  return std::make_shared<SHAMapTxPlusMetaLeafNode>(
41  std::move(item), owner);
42 
44  return std::make_shared<SHAMapAccountStateLeafNode>(
45  std::move(item), owner);
46 
47  LogicError(
48  "Attempt to create leaf node of unknown type " +
50  static_cast<std::underlying_type_t<SHAMapNodeType>>(type)));
51 }
52 
54  : f_(f), journal_(f.journal()), state_(SHAMapState::Modifying), type_(t)
55 {
56  root_ = std::make_shared<SHAMapInnerNode>(cowid_);
57 }
58 
59 // The `hash` parameter is unused. It is part of the interface so it's clear
60 // from the parameters that this is the constructor to use when the hash is
61 // known. The fact that the parameter is unused is an implementation detail that
62 // should not change the interface.
64  : f_(f), journal_(f.journal()), state_(SHAMapState::Synching), type_(t)
65 {
66  root_ = std::make_shared<SHAMapInnerNode>(cowid_);
67 }
68 
70 SHAMap::snapShot(bool isMutable) const
71 {
72  auto ret = std::make_shared<SHAMap>(type_, f_);
73  SHAMap& newMap = *ret;
74 
75  if (!isMutable)
77 
78  newMap.cowid_ = cowid_ + 1;
79  newMap.ledgerSeq_ = ledgerSeq_;
80  newMap.root_ = root_;
81  newMap.backed_ = backed_;
82 
83  if ((state_ != SHAMapState::Immutable) ||
84  (newMap.state_ != SHAMapState::Immutable))
85  {
86  // If either map may change, they cannot share nodes
87  newMap.unshare();
88  }
89 
90  return ret;
91 }
92 
93 void
95  SharedPtrNodeStack& stack,
96  uint256 const& target,
98 {
99  // walk the tree up from through the inner nodes to the root_
100  // update hashes and links
101  // stack is a path of inner nodes up to, but not including, child
102  // child can be an inner node or a leaf
103 
104  assert(
107  assert(child && (child->cowid() == cowid_));
108 
109  while (!stack.empty())
110  {
111  auto node =
112  std::dynamic_pointer_cast<SHAMapInnerNode>(stack.top().first);
113  SHAMapNodeID nodeID = stack.top().second;
114  stack.pop();
115  assert(node != nullptr);
116 
117  int branch = selectBranch(nodeID, target);
118  assert(branch >= 0);
119 
120  node = unshareNode(std::move(node), nodeID);
121  node->setChild(branch, child);
122 
123  child = std::move(node);
124  }
125 }
126 
129 {
130  assert(stack == nullptr || stack->empty());
131  auto inNode = root_;
132  SHAMapNodeID nodeID;
133 
134  while (inNode->isInner())
135  {
136  if (stack != nullptr)
137  stack->push({inNode, nodeID});
138 
139  auto const inner = std::static_pointer_cast<SHAMapInnerNode>(inNode);
140  auto const branch = selectBranch(nodeID, id);
141  if (inner->isEmptyBranch(branch))
142  return nullptr;
143 
144  inNode = descendThrow(inner, branch);
145  nodeID = nodeID.getChildNodeID(branch);
146  }
147 
148  if (stack != nullptr)
149  stack->push({inNode, nodeID});
150  return static_cast<SHAMapLeafNode*>(inNode.get());
151 }
152 
154 SHAMap::findKey(uint256 const& id) const
155 {
156  SHAMapLeafNode* leaf = walkTowardsKey(id);
157  if (leaf && leaf->peekItem()->key() != id)
158  leaf = nullptr;
159  return leaf;
160 }
161 
164 {
165  assert(backed_);
166  auto obj = f_.db().fetchNodeObject(hash.as_uint256(), ledgerSeq_);
167  return finishFetch(hash, obj);
168 }
169 
172  SHAMapHash const& hash,
173  std::shared_ptr<NodeObject> const& object) const
174 {
175  assert(backed_);
176  if (!object)
177  {
178  if (full_)
179  {
180  full_ = false;
182  }
183  return {};
184  }
185 
187  try
188  {
189  node =
190  SHAMapTreeNode::makeFromPrefix(makeSlice(object->getData()), hash);
191  if (node)
192  canonicalize(hash, node);
193  return node;
194  }
195  catch (std::exception const&)
196  {
197  JLOG(journal_.warn()) << "Invalid DB node " << hash;
199  }
200 }
201 
202 // See if a sync filter has a node
205 {
206  if (auto nodeData = filter->getNode(hash))
207  {
208  try
209  {
210  auto node =
211  SHAMapTreeNode::makeFromPrefix(makeSlice(*nodeData), hash);
212  if (node)
213  {
214  filter->gotNode(
215  true,
216  hash,
217  ledgerSeq_,
218  std::move(*nodeData),
219  node->getType());
220  if (backed_)
221  canonicalize(hash, node);
222  }
223  return node;
224  }
225  catch (std::exception const& x)
226  {
227  JLOG(f_.journal().warn())
228  << "Invalid node/data, hash=" << hash << ": " << x.what();
229  }
230  }
231  return {};
232 }
233 
234 // Get a node without throwing
235 // Used on maps where missing nodes are expected
238 {
239  auto node = cacheLookup(hash);
240  if (node)
241  return node;
242 
243  if (backed_)
244  {
245  node = fetchNodeFromDB(hash);
246  if (node)
247  {
248  canonicalize(hash, node);
249  return node;
250  }
251  }
252 
253  if (filter)
254  node = checkFilter(hash, filter);
255 
256  return node;
257 }
258 
260 SHAMap::fetchNodeNT(SHAMapHash const& hash) const
261 {
262  auto node = cacheLookup(hash);
263 
264  if (!node && backed_)
265  node = fetchNodeFromDB(hash);
266 
267  return node;
268 }
269 
270 // Throw if the node is missing
272 SHAMap::fetchNode(SHAMapHash const& hash) const
273 {
274  auto node = fetchNodeNT(hash);
275 
276  if (!node)
277  Throw<SHAMapMissingNode>(type_, hash);
278 
279  return node;
280 }
281 
283 SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const
284 {
285  SHAMapTreeNode* ret = descend(parent, branch);
286 
287  if (!ret && !parent->isEmptyBranch(branch))
288  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
289 
290  return ret;
291 }
292 
295  const
296 {
297  std::shared_ptr<SHAMapTreeNode> ret = descend(parent, branch);
298 
299  if (!ret && !parent->isEmptyBranch(branch))
300  Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
301 
302  return ret;
303 }
304 
306 SHAMap::descend(SHAMapInnerNode* parent, int branch) const
307 {
308  SHAMapTreeNode* ret = parent->getChildPointer(branch);
309  if (ret || !backed_)
310  return ret;
311 
313  fetchNodeNT(parent->getChildHash(branch));
314  if (!node)
315  return nullptr;
316 
317  node = parent->canonicalizeChild(branch, std::move(node));
318  return node.get();
319 }
320 
323  const
324 {
325  std::shared_ptr<SHAMapTreeNode> node = parent->getChild(branch);
326  if (node || !backed_)
327  return node;
328 
329  node = fetchNode(parent->getChildHash(branch));
330  if (!node)
331  return nullptr;
332 
333  node = parent->canonicalizeChild(branch, std::move(node));
334  return node;
335 }
336 
337 // Gets the node that would be hooked to this branch,
338 // but doesn't hook it up.
341  std::shared_ptr<SHAMapInnerNode> const& parent,
342  int branch) const
343 {
344  std::shared_ptr<SHAMapTreeNode> ret = parent->getChild(branch);
345  if (!ret && backed_)
346  ret = fetchNode(parent->getChildHash(branch));
347  return ret;
348 }
349 
352  SHAMapInnerNode* parent,
353  SHAMapNodeID const& parentID,
354  int branch,
355  SHAMapSyncFilter* filter) const
356 {
357  assert(parent->isInner());
358  assert((branch >= 0) && (branch < branchFactor));
359  assert(!parent->isEmptyBranch(branch));
360 
361  SHAMapTreeNode* child = parent->getChildPointer(branch);
362 
363  if (!child)
364  {
365  auto const& childHash = parent->getChildHash(branch);
367  fetchNodeNT(childHash, filter);
368 
369  if (childNode)
370  {
371  childNode = parent->canonicalizeChild(branch, std::move(childNode));
372  child = childNode.get();
373  }
374  }
375 
376  return std::make_pair(child, parentID.getChildNodeID(branch));
377 }
378 
381  SHAMapInnerNode* parent,
382  int branch,
383  SHAMapSyncFilter* filter,
384  bool& pending,
385  descendCallback&& callback) const
386 {
387  pending = false;
388 
389  SHAMapTreeNode* ret = parent->getChildPointer(branch);
390  if (ret)
391  return ret;
392 
393  auto const& hash = parent->getChildHash(branch);
394 
395  auto ptr = cacheLookup(hash);
396  if (!ptr)
397  {
398  if (filter)
399  ptr = checkFilter(hash, filter);
400 
401  if (!ptr && backed_)
402  {
403  f_.db().asyncFetch(
404  hash.as_uint256(),
405  ledgerSeq_,
406  [this, hash, cb{std::move(callback)}](
407  std::shared_ptr<NodeObject> const& object) {
408  auto node = finishFetch(hash, object);
409  cb(node, hash);
410  });
411  pending = true;
412  return nullptr;
413  }
414  }
415 
416  if (ptr)
417  ptr = parent->canonicalizeChild(branch, std::move(ptr));
418 
419  return ptr.get();
420 }
421 
422 template <class Node>
425 {
426  // make sure the node is suitable for the intended operation (copy on write)
427  assert(node->cowid() <= cowid_);
428  if (node->cowid() != cowid_)
429  {
430  // have a CoW
431  assert(state_ != SHAMapState::Immutable);
432  node = std::static_pointer_cast<Node>(node->clone(cowid_));
433  if (nodeID.isRoot())
434  root_ = node;
435  }
436  return node;
437 }
438 
442  SharedPtrNodeStack& stack,
443  int branch) const
444 {
445  // Return the first item at or below this node
446  if (node->isLeaf())
447  {
448  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
449  stack.push({node, {leafDepth, n->peekItem()->key()}});
450  return n.get();
451  }
452  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
453  if (stack.empty())
454  stack.push({inner, SHAMapNodeID{}});
455  else
456  stack.push({inner, stack.top().second.getChildNodeID(branch)});
457  for (int i = 0; i < branchFactor;)
458  {
459  if (!inner->isEmptyBranch(i))
460  {
461  node = descendThrow(inner, i);
462  assert(!stack.empty());
463  if (node->isLeaf())
464  {
465  auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
466  stack.push({n, {leafDepth, n->peekItem()->key()}});
467  return n.get();
468  }
469  inner = std::static_pointer_cast<SHAMapInnerNode>(node);
470  stack.push({inner, stack.top().second.getChildNodeID(branch)});
471  i = 0; // scan all 16 branches of this new node
472  }
473  else
474  ++i; // scan next branch
475  }
476  return nullptr;
477 }
478 
480 
483 {
484  // If there is only one item below this node, return it
485 
486  while (!node->isLeaf())
487  {
488  SHAMapTreeNode* nextNode = nullptr;
489  auto inner = static_cast<SHAMapInnerNode*>(node);
490  for (int i = 0; i < branchFactor; ++i)
491  {
492  if (!inner->isEmptyBranch(i))
493  {
494  if (nextNode)
495  return no_item;
496 
497  nextNode = descendThrow(inner, i);
498  }
499  }
500 
501  if (!nextNode)
502  {
503  assert(false);
504  return no_item;
505  }
506 
507  node = nextNode;
508  }
509 
510  // An inner node must have at least one leaf
511  // below it, unless it's the root_
512  auto const leaf = static_cast<SHAMapLeafNode const*>(node);
513  assert(leaf->peekItem() || (leaf == root_.get()));
514  return leaf->peekItem();
515 }
516 
517 SHAMapLeafNode const*
519 {
520  assert(stack.empty());
521  SHAMapLeafNode* node = firstBelow(root_, stack);
522  if (!node)
523  {
524  while (!stack.empty())
525  stack.pop();
526  return nullptr;
527  }
528  return node;
529 }
530 
531 SHAMapLeafNode const*
533 {
534  assert(!stack.empty());
535  assert(stack.top().first->isLeaf());
536  stack.pop();
537  while (!stack.empty())
538  {
539  auto [node, nodeID] = stack.top();
540  assert(!node->isLeaf());
541  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
542  for (auto i = selectBranch(nodeID, id) + 1; i < branchFactor; ++i)
543  {
544  if (!inner->isEmptyBranch(i))
545  {
546  node = descendThrow(inner, i);
547  auto leaf = firstBelow(node, stack, i);
548  if (!leaf)
549  Throw<SHAMapMissingNode>(type_, id);
550  assert(leaf->isLeaf());
551  return leaf;
552  }
553  }
554  stack.pop();
555  }
556  // must be last item
557  return nullptr;
558 }
559 
561 SHAMap::peekItem(uint256 const& id) const
562 {
563  SHAMapLeafNode* leaf = findKey(id);
564 
565  if (!leaf)
566  return no_item;
567 
568  return leaf->peekItem();
569 }
570 
572 SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const
573 {
574  SHAMapLeafNode* leaf = findKey(id);
575 
576  if (!leaf)
577  return no_item;
578 
579  hash = leaf->getHash();
580  return leaf->peekItem();
581 }
582 
584 SHAMap::upper_bound(uint256 const& id) const
585 {
586  // Get a const_iterator to the next item in the tree after a given item
587  // item need not be in tree
588  SharedPtrNodeStack stack;
589  walkTowardsKey(id, &stack);
590  while (!stack.empty())
591  {
592  auto [node, nodeID] = stack.top();
593  if (node->isLeaf())
594  {
595  auto leaf = static_cast<SHAMapLeafNode*>(node.get());
596  if (leaf->peekItem()->key() > id)
597  return const_iterator(
598  this, leaf->peekItem().get(), std::move(stack));
599  }
600  else
601  {
602  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
603  for (auto branch = selectBranch(nodeID, id) + 1;
604  branch < branchFactor;
605  ++branch)
606  {
607  if (!inner->isEmptyBranch(branch))
608  {
609  node = descendThrow(inner, branch);
610  auto leaf = firstBelow(node, stack, branch);
611  if (!leaf)
612  Throw<SHAMapMissingNode>(type_, id);
613  return const_iterator(
614  this, leaf->peekItem().get(), std::move(stack));
615  }
616  }
617  }
618  stack.pop();
619  }
620  return end();
621 }
622 
623 bool
624 SHAMap::hasItem(uint256 const& id) const
625 {
626  return (findKey(id) != nullptr);
627 }
628 
629 bool
631 {
632  // delete the item with this ID
633  assert(state_ != SHAMapState::Immutable);
634 
635  SharedPtrNodeStack stack;
636  walkTowardsKey(id, &stack);
637 
638  if (stack.empty())
639  Throw<SHAMapMissingNode>(type_, id);
640 
641  auto leaf = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
642  stack.pop();
643 
644  if (!leaf || (leaf->peekItem()->key() != id))
645  return false;
646 
647  SHAMapNodeType type = leaf->getType();
648 
649  // What gets attached to the end of the chain
650  // (For now, nothing, since we deleted the leaf)
652 
653  while (!stack.empty())
654  {
655  auto node =
656  std::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
657  SHAMapNodeID nodeID = stack.top().second;
658  stack.pop();
659 
660  node = unshareNode(std::move(node), nodeID);
661  node->setChild(selectBranch(nodeID, id), prevNode);
662 
663  if (!nodeID.isRoot())
664  {
665  // we may have made this a node with 1 or 0 children
666  // And, if so, we need to remove this branch
667  const int bc = node->getBranchCount();
668  if (bc == 0)
669  {
670  // no children below this branch
671  prevNode.reset();
672  }
673  else if (bc == 1)
674  {
675  // If there's only one item, pull up on the thread
676  auto item = onlyBelow(node.get());
677 
678  if (item)
679  {
680  for (int i = 0; i < branchFactor; ++i)
681  {
682  if (!node->isEmptyBranch(i))
683  {
684  node->setChild(i, nullptr);
685  break;
686  }
687  }
688 
689  prevNode = makeTypedLeaf(type, item, node->cowid());
690  }
691  else
692  {
693  prevNode = std::move(node);
694  }
695  }
696  else
697  {
698  // This node is now the end of the branch
699  prevNode = std::move(node);
700  }
701  }
702  }
703 
704  return true;
705 }
706 
707 bool
709 {
710  assert(state_ != SHAMapState::Immutable);
711  assert(type != SHAMapNodeType::tnINNER);
712 
713  // add the specified item, does not update
714  uint256 tag = item->key();
715 
716  SharedPtrNodeStack stack;
717  walkTowardsKey(tag, &stack);
718 
719  if (stack.empty())
720  Throw<SHAMapMissingNode>(type_, tag);
721 
722  auto [node, nodeID] = stack.top();
723  stack.pop();
724 
725  if (node->isLeaf())
726  {
727  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
728  if (leaf->peekItem()->key() == tag)
729  return false;
730  }
731  node = unshareNode(std::move(node), nodeID);
732  if (node->isInner())
733  {
734  // easy case, we end on an inner node
735  auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
736  int branch = selectBranch(nodeID, tag);
737  assert(inner->isEmptyBranch(branch));
738  auto newNode = makeTypedLeaf(type, std::move(item), cowid_);
739  inner->setChild(branch, newNode);
740  }
741  else
742  {
743  // this is a leaf node that has to be made an inner node holding two
744  // items
745  auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
746  std::shared_ptr<SHAMapItem const> otherItem = leaf->peekItem();
747  assert(otherItem && (tag != otherItem->key()));
748 
749  node = std::make_shared<SHAMapInnerNode>(node->cowid());
750 
751  unsigned int b1, b2;
752 
753  while ((b1 = selectBranch(nodeID, tag)) ==
754  (b2 = selectBranch(nodeID, otherItem->key())))
755  {
756  stack.push({node, nodeID});
757 
758  // we need a new inner node, since both go on same branch at this
759  // level
760  nodeID = nodeID.getChildNodeID(b1);
761  node = std::make_shared<SHAMapInnerNode>(cowid_);
762  }
763 
764  // we can add the two leaf nodes here
765  assert(node->isInner());
766 
767  auto inner = static_cast<SHAMapInnerNode*>(node.get());
768  inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
769  inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
770  }
771 
772  dirtyUp(stack, tag, node);
773  return true;
774 }
775 
776 bool
778 {
779  return addGiveItem(type, std::make_shared<SHAMapItem const>(std::move(i)));
780 }
781 
784 {
785  auto hash = root_->getHash();
786  if (hash.isZero())
787  {
788  const_cast<SHAMap&>(*this).unshare();
789  hash = root_->getHash();
790  }
791  return hash;
792 }
793 
794 bool
796  SHAMapNodeType type,
798 {
799  // can't change the tag but can change the hash
800  uint256 tag = item->key();
801 
802  assert(state_ != SHAMapState::Immutable);
803 
804  SharedPtrNodeStack stack;
805  walkTowardsKey(tag, &stack);
806 
807  if (stack.empty())
808  Throw<SHAMapMissingNode>(type_, tag);
809 
810  auto node = std::dynamic_pointer_cast<SHAMapLeafNode>(stack.top().first);
811  auto nodeID = stack.top().second;
812  stack.pop();
813 
814  if (!node || (node->peekItem()->key() != tag))
815  {
816  assert(false);
817  return false;
818  }
819 
820  if (node->getType() != type)
821  {
822  JLOG(journal_.fatal()) << "SHAMap::setItem: cross-type change!";
823  return false;
824  }
825 
826  node = unshareNode(std::move(node), nodeID);
827 
828  if (node->setItem(std::move(item)))
829  dirtyUp(stack, tag, node);
830 
831  return true;
832 }
833 
834 bool
836 {
837  if (hash == root_->getHash())
838  return true;
839 
840  if (auto stream = journal_.trace())
841  {
843  {
844  stream << "Fetch root TXN node " << hash;
845  }
846  else if (type_ == SHAMapType::STATE)
847  {
848  stream << "Fetch root STATE node " << hash;
849  }
850  else
851  {
852  stream << "Fetch root SHAMap node " << hash;
853  }
854  }
855 
856  auto newRoot = fetchNodeNT(hash, filter);
857 
858  if (newRoot)
859  {
860  root_ = newRoot;
861  assert(root_->getHash() == hash);
862  return true;
863  }
864 
865  return false;
866 }
867 
882 {
883  assert(node->cowid() == 0);
884  assert(backed_);
885 
886  canonicalize(node->getHash(), node);
887 
888  Serializer s;
889  node->serializeWithPrefix(s);
890  f_.db().store(
891  t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_);
892  return node;
893 }
894 
895 // We can't modify an inner node someone else might have a
896 // pointer to because flushing modifies inner nodes -- it
897 // makes them point to canonical/shared nodes.
898 template <class Node>
901 {
902  // A shared node should never need to be flushed
903  // because that would imply someone modified it
904  assert(node->cowid() != 0);
905 
906  if (node->cowid() != cowid_)
907  {
908  // Node is not uniquely ours, so unshare it before
909  // possibly modifying it
910  node = std::static_pointer_cast<Node>(node->clone(cowid_));
911  }
912  return node;
913 }
914 
915 int
917 {
918  // Don't share nodes with parent map
919  return walkSubTree(false, hotUNKNOWN);
920 }
921 
922 int
924 {
925  // We only write back if this map is backed.
926  return walkSubTree(backed_, t);
927 }
928 
929 int
931 {
932  assert(!doWrite || backed_);
933 
934  int flushed = 0;
935 
936  if (!root_ || (root_->cowid() == 0))
937  return flushed;
938 
939  if (root_->isLeaf())
940  { // special case -- root_ is leaf
941  root_ = preFlushNode(std::move(root_));
942  root_->updateHash();
943  root_->unshare();
944 
945  if (doWrite)
946  root_ = writeNode(t, std::move(root_));
947 
948  return 1;
949  }
950 
951  auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
952 
953  if (node->isEmpty())
954  { // replace empty root with a new empty root
955  root_ = std::make_shared<SHAMapInnerNode>(0);
956  return 1;
957  }
958 
959  // Stack of {parent,index,child} pointers representing
960  // inner nodes we are in the process of flushing
961  using StackEntry = std::pair<std::shared_ptr<SHAMapInnerNode>, int>;
963 
964  node = preFlushNode(std::move(node));
965 
966  int pos = 0;
967 
968  // We can't flush an inner node until we flush its children
969  while (1)
970  {
971  while (pos < branchFactor)
972  {
973  if (node->isEmptyBranch(pos))
974  {
975  ++pos;
976  }
977  else
978  {
979  // No need to do I/O. If the node isn't linked,
980  // it can't need to be flushed
981  int branch = pos;
982  auto child = node->getChild(pos++);
983 
984  if (child && (child->cowid() != 0))
985  {
986  // This is a node that needs to be flushed
987 
988  child = preFlushNode(std::move(child));
989 
990  if (child->isInner())
991  {
992  // save our place and work on this node
993 
994  stack.emplace(std::move(node), branch);
995  // The semantics of this changes when we move to c++-20
996  // Right now no move will occur; With c++-20 child will
997  // be moved from.
998  node = std::static_pointer_cast<SHAMapInnerNode>(
999  std::move(child));
1000  pos = 0;
1001  }
1002  else
1003  {
1004  // flush this leaf
1005  ++flushed;
1006 
1007  assert(node->cowid() == cowid_);
1008  child->updateHash();
1009  child->unshare();
1010 
1011  if (doWrite)
1012  child = writeNode(t, std::move(child));
1013 
1014  node->shareChild(branch, child);
1015  }
1016  }
1017  }
1018  }
1019 
1020  // update the hash of this inner node
1021  node->updateHashDeep();
1022 
1023  // This inner node can now be shared
1024  node->unshare();
1025 
1026  if (doWrite)
1027  node = std::static_pointer_cast<SHAMapInnerNode>(
1028  writeNode(t, std::move(node)));
1029 
1030  ++flushed;
1031 
1032  if (stack.empty())
1033  break;
1034 
1035  auto parent = std::move(stack.top().first);
1036  pos = stack.top().second;
1037  stack.pop();
1038 
1039  // Hook this inner node to its parent
1040  assert(parent->cowid() == cowid_);
1041  parent->shareChild(pos, node);
1042 
1043  // Continue with parent's next child, if any
1044  node = std::move(parent);
1045  ++pos;
1046  }
1047 
1048  // Last inner node is the new root_
1049  root_ = std::move(node);
1050 
1051  return flushed;
1052 }
1053 
1054 void
1055 SHAMap::dump(bool hash) const
1056 {
1057  int leafCount = 0;
1058  JLOG(journal_.info()) << " MAP Contains";
1059 
1061  stack.push({root_.get(), SHAMapNodeID()});
1062 
1063  do
1064  {
1065  auto [node, nodeID] = stack.top();
1066  stack.pop();
1067 
1068  JLOG(journal_.info()) << node->getString(nodeID);
1069  if (hash)
1070  {
1071  JLOG(journal_.info()) << "Hash: " << node->getHash();
1072  }
1073 
1074  if (node->isInner())
1075  {
1076  auto inner = static_cast<SHAMapInnerNode*>(node);
1077  for (int i = 0; i < branchFactor; ++i)
1078  {
1079  if (!inner->isEmptyBranch(i))
1080  {
1081  auto child = inner->getChildPointer(i);
1082  if (child)
1083  {
1084  assert(child->getHash() == inner->getChildHash(i));
1085  stack.push({child, nodeID.getChildNodeID(i)});
1086  }
1087  }
1088  }
1089  }
1090  else
1091  ++leafCount;
1092  } while (!stack.empty());
1093 
1094  JLOG(journal_.info()) << leafCount << " resident leaves";
1095 }
1096 
1099 {
1100  auto ret = f_.getTreeNodeCache(ledgerSeq_)->fetch(hash.as_uint256());
1101  assert(!ret || !ret->cowid());
1102  return ret;
1103 }
1104 
1105 void
1107  SHAMapHash const& hash,
1108  std::shared_ptr<SHAMapTreeNode>& node) const
1109 {
1110  assert(backed_);
1111  assert(node->cowid() == 0);
1112  assert(node->getHash() == hash);
1113 
1115  ->canonicalize_replace_client(hash.as_uint256(), node);
1116 }
1117 
1118 void
1120 {
1121  (void)getHash(); // update node hashes
1122  auto node = root_.get();
1123  assert(node != nullptr);
1124  assert(!node->isLeaf());
1125  SharedPtrNodeStack stack;
1126  for (auto leaf = peekFirstItem(stack); leaf != nullptr;
1127  leaf = peekNextItem(leaf->peekItem()->key(), stack))
1128  ;
1129  node->invariants(true);
1130 }
1131 
1132 } // 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:129
ripple::hotUNKNOWN
@ hotUNKNOWN
Definition: NodeObject.h:33
ripple::SHAMap::invariants
void invariants() const
Definition: SHAMap.cpp:1119
ripple::Family::getTreeNodeCache
virtual std::shared_ptr< TreeNodeCache > getTreeNodeCache(std::uint32_t ledgerSeq)=0
Return a pointer to the Family Tree Node Cache.
ripple::SHAMap::branchFactor
static constexpr unsigned int branchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map)
Definition: SHAMap.h:116
ripple::SHAMap::descendNoStore
std::shared_ptr< SHAMapTreeNode > descendNoStore(std::shared_ptr< SHAMapInnerNode > const &, int branch) const
Definition: SHAMap.cpp:340
ripple::ListDisposition::pending
@ pending
List will be valid in the future.
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:285
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:240
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:532
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:783
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:176
ripple::SHAMap::addItem
bool addItem(SHAMapNodeType type, SHAMapItem &&i)
Definition: SHAMap.cpp:777
ripple::SHAMapNodeType::tnACCOUNT_STATE
@ tnACCOUNT_STATE
std::pair::first
T first
ripple::SHAMapType::TRANSACTION
@ TRANSACTION
ripple::SHAMapInnerNode::canonicalizeChild
virtual std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:363
ripple::SHAMap::walkSubTree
int walkSubTree(bool doWrite, NodeObjectType t)
Definition: SHAMap.cpp:930
ripple::SHAMap::dump
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1055
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:482
ripple::SHAMapNodeType
SHAMapNodeType
Definition: SHAMapTreeNode.h:126
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:561
ripple::NodeStore::Database::asyncFetch
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:94
ripple::SHAMap::fetchNode
std::shared_ptr< SHAMapTreeNode > fetchNode(SHAMapHash const &hash) const
Definition: SHAMap.cpp:272
std::stack::emplace
T emplace(T... args)
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::shared_ptr::get
T get(T... args)
ripple::SHAMap::fetchNodeNT
std::shared_ptr< SHAMapTreeNode > fetchNodeNT(SHAMapHash const &hash) const
Definition: SHAMap.cpp:260
ripple::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:25
ripple::SHAMap::cacheLookup
std::shared_ptr< SHAMapTreeNode > cacheLookup(SHAMapHash const &hash) const
Definition: SHAMap.cpp:1098
ripple::Family::missingNode
virtual void missingNode(std::uint32_t refNum)=0
ripple::Family::db
virtual NodeStore::Database & db()=0
ripple::SHAMapLeafNode
Definition: SHAMapLeafNode.h:32
std::shared_ptr::reset
T reset(T... args)
ripple::SHAMapHash
Definition: SHAMapTreeNode.h:47
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::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:195
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:624
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:353
ripple::SHAMapInnerNode
Definition: SHAMapInnerNode.h:39
ripple::SHAMap::peekFirstItem
SHAMapLeafNode const * peekFirstItem(SharedPtrNodeStack &stack) const
Definition: SHAMap.cpp:518
ripple::SHAMapItem
Definition: SHAMapItem.h:35
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:479
ripple::SHAMap::const_iterator
Definition: SHAMap.h:593
ripple::SHAMap::firstBelow
SHAMapLeafNode * firstBelow(std::shared_ptr< SHAMapTreeNode >, SharedPtrNodeStack &stack, int branch=0) const
Definition: SHAMap.cpp:440
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:133
std::to_string
T to_string(T... args)
ripple::SHAMap::descendThrow
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:283
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::SHAMap::journal_
beast::Journal journal_
Definition: SHAMap.h:99
ripple::SHAMapTreeNode::isLeaf
virtual bool isLeaf() const =0
Determines if this is a leaf node.
ripple::SHAMap::canonicalize
void canonicalize(SHAMapHash const &hash, std::shared_ptr< SHAMapTreeNode > &) const
Definition: SHAMap.cpp:1106
ripple::Family
Definition: Family.h:32
std::uint32_t
ripple::NodeStore::Database::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous)
Fetch a node object.
Definition: Database.cpp:145
ripple::SHAMapSyncFilter::getNode
virtual boost::optional< Blob > getNode(SHAMapHash const &nodeHash) const =0
ripple::SHAMap::fetchRoot
bool fetchRoot(SHAMapHash const &hash, SHAMapSyncFilter *filter)
Definition: SHAMap.cpp:835
ripple::SHAMapTreeNode::getHash
SHAMapHash const & getHash() const
Return the hash of this node.
Definition: SHAMapTreeNode.h:223
ripple::Serializer
Definition: Serializer.h:39
ripple::SHAMapTreeNode::makeFromPrefix
static std::shared_ptr< SHAMapTreeNode > makeFromPrefix(Slice rawNode, SHAMapHash const &hash)
Definition: SHAMapTreeNode.cpp:151
ripple::SHAMap::upper_bound
const_iterator upper_bound(uint256 const &id) const
Definition: SHAMap.cpp:584
ripple::SHAMap::finishFetch
std::shared_ptr< SHAMapTreeNode > finishFetch(SHAMapHash const &hash, std::shared_ptr< NodeObject > const &object) const
Definition: SHAMap.cpp:171
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::SHAMap::full_
bool full_
Definition: SHAMap.h:111
ripple::SHAMap::checkFilter
std::shared_ptr< SHAMapTreeNode > checkFilter(SHAMapHash const &hash, SHAMapSyncFilter *filter) const
Definition: SHAMap.cpp:204
ripple::SHAMap::dirtyUp
void dirtyUp(SharedPtrNodeStack &stack, uint256 const &target, std::shared_ptr< SHAMapTreeNode > terminal)
Update hashes up to the root.
Definition: SHAMap.cpp:94
ripple::SHAMap::findKey
SHAMapLeafNode * findKey(uint256 const &id) const
Return nullptr if key not found.
Definition: SHAMap.cpp:154
ripple::SHAMap::updateGiveItem
bool updateGiveItem(SHAMapNodeType type, std::shared_ptr< SHAMapItem const >)
Definition: SHAMap.cpp:795
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:707
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:708
ripple::SHAMap::unshare
int unshare()
Convert any modified nodes to shared.
Definition: SHAMap.cpp:916
ripple::SHAMap::descendAsync
SHAMapTreeNode * descendAsync(SHAMapInnerNode *parent, int branch, SHAMapSyncFilter *filter, bool &pending, descendCallback &&) const
Definition: SHAMap.cpp:380
ripple::SHAMap::writeNode
std::shared_ptr< SHAMapTreeNode > writeNode(NodeObjectType t, std::shared_ptr< SHAMapTreeNode > node) const
write and canonicalize modified node
Definition: SHAMap.cpp:881
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:900
ripple::SHAMap::flushDirty
int flushDirty(NodeObjectType t)
Flush modified nodes to the nodestore and convert them to shared.
Definition: SHAMap.cpp:923
ripple::Family::journal
virtual beast::Journal const & journal()=0
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: SHAMapTreeNode.h:58
ripple::SHAMapInnerNode::getChildPointer
SHAMapTreeNode * getChildPointer(int branch)
Definition: SHAMapInnerNode.cpp:333
ripple::SHAMap::unshareNode
std::shared_ptr< Node > unshareNode(std::shared_ptr< Node >, SHAMapNodeID const &nodeID)
Unshare the node, allowing it to be modified.
Definition: SHAMap.cpp:424
ripple::SHAMap::descend
SHAMapTreeNode * descend(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:306
ripple::SHAMap::ledgerSeq_
std::uint32_t ledgerSeq_
The sequence of the ledger that this map references, if any.
Definition: SHAMap.h:105
ripple::SHAMapSyncFilter
Definition: SHAMapSyncFilter.h:30
ripple::SHAMap::delItem
bool delItem(uint256 const &id)
Definition: SHAMap.cpp:630
ripple::SHAMap::cowid_
std::uint32_t cowid_
ID to distinguish this map for all others we're sharing nodes with.
Definition: SHAMap.h:102
ripple::SHAMap::state_
SHAMapState state_
Definition: SHAMap.h:108
ripple::SHAMapNodeID::isRoot
bool isRoot() const
Definition: SHAMapNodeID.h:48
std::exception::what
T what(T... args)
ripple::SHAMap::root_
std::shared_ptr< SHAMapTreeNode > root_
Definition: SHAMap.h:107
ripple::SHAMapState
SHAMapState
Describes the current state of a given SHAMap.
Definition: SHAMap.h:46