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