diff --git a/src/ripple/app/consensus/LedgerConsensus.cpp b/src/ripple/app/consensus/LedgerConsensus.cpp index 6ea4d38a3..132ea2f4d 100644 --- a/src/ripple/app/consensus/LedgerConsensus.cpp +++ b/src/ripple/app/consensus/LedgerConsensus.cpp @@ -1027,35 +1027,19 @@ private: // Set up to write SHAMap changes to our database, // perform updates, extract changes - newLCL->peekTransactionMap ()->armDirty (); // start tracking changes - newLCL->peekAccountStateMap ()->armDirty (); WriteLog (lsDEBUG, LedgerConsensus) << "Applying consensus set transactions to the" << " last closed ledger"; applyTransactions (set, newLCL, newLCL, retriableTransactions, false); newLCL->updateSkipList (); newLCL->setClosed (); - std::shared_ptr acctNodes - = newLCL->peekAccountStateMap ()->disarmDirty (); // stop tracking changes - std::shared_ptr txnNodes - = newLCL->peekTransactionMap ()->disarmDirty (); - // write out dirty nodes (temporarily done here) - int fc; - - while ((fc = newLCL->peekAccountStateMap()->flushDirty ( - *acctNodes, 256, hotACCOUNT_NODE, newLCL->getLedgerSeq ())) > 0) - { - WriteLog (lsTRACE, LedgerConsensus) - << "Flushed " << fc << " dirty state nodes"; - } - - while ((fc = newLCL->peekTransactionMap()->flushDirty ( - *txnNodes, 256, hotTRANSACTION_NODE, newLCL->getLedgerSeq ())) > 0) - { - WriteLog (lsTRACE, LedgerConsensus) - << "Flushed " << fc << " dirty transaction nodes"; - } + int asf = newLCL->peekAccountStateMap ()->flushDirty ( + hotACCOUNT_NODE, newLCL->getLedgerSeq()); + int tmf = newLCL->peekTransactionMap ()->flushDirty ( + hotTRANSACTION_NODE, newLCL->getLedgerSeq()); + WriteLog (lsDEBUG, LedgerConsensus) << "Flushed " << asf << " account and " << + tmf << "transaction nodes"; // Accept ledger newLCL->setAccepted (closeTime, mCloseResolution, closeTimeCorrect); diff --git a/src/ripple/app/ledger/InboundLedger.cpp b/src/ripple/app/ledger/InboundLedger.cpp index d8553168a..fa64eb01b 100644 --- a/src/ripple/app/ledger/InboundLedger.cpp +++ b/src/ripple/app/ledger/InboundLedger.cpp @@ -1232,9 +1232,6 @@ Json::Value InboundLedger::getJson (int) { ret["have_state"] = mHaveState; ret["have_transactions"] = mHaveTransactions; - if (!mHaveState) - ret["state_nodes"] = static_cast - (mLedger->peekAccountStateMap()->size()); } if (mAborted) diff --git a/src/ripple/app/ledger/Ledger.cpp b/src/ripple/app/ledger/Ledger.cpp index c01e15b28..9d682f589 100644 --- a/src/ripple/app/ledger/Ledger.cpp +++ b/src/ripple/app/ledger/Ledger.cpp @@ -57,14 +57,9 @@ Ledger::Ledger (RippleAddress const& masterID, std::uint64_t startAmount) WriteLog (lsTRACE, Ledger) << "root account: " << startAccount->peekSLE ().getJson (0); - mAccountStateMap->armDirty (); - writeBack (lepCREATE, startAccount->getSLE ()); - auto dirtyNodes = mAccountStateMap->disarmDirty(); - mAccountStateMap->flushDirty ( - *dirtyNodes, 256, hotACCOUNT_NODE, mLedgerSeq); - // TODO(tom): why 256? + mAccountStateMap->flushDirty (hotACCOUNT_NODE, mLedgerSeq); initializeFees (); } @@ -243,16 +238,12 @@ Ledger::~Ledger () { if (mTransactionMap) { - logTimedDestroy (mTransactionMap, - "mTransactionMap with " + - std::to_string(mTransactionMap->size ()) + " items"); + logTimedDestroy (mTransactionMap, "mTransactionMap"); } if (mAccountStateMap) { - logTimedDestroy (mAccountStateMap, - "mAccountStateMap with " + - std::to_string (mAccountStateMap->size ()) + " items"); + logTimedDestroy (mAccountStateMap, "mAccountStateMap"); } } diff --git a/src/ripple/app/ledger/Ledger.h b/src/ripple/app/ledger/Ledger.h index a8d0bfb08..25151af73 100644 --- a/src/ripple/app/ledger/Ledger.h +++ b/src/ripple/app/ledger/Ledger.h @@ -239,16 +239,6 @@ public: { return mAccountStateMap; } - void dropCache () - { - assert (isImmutable ()); - - if (mTransactionMap) - mTransactionMap->dropCache (); - - if (mAccountStateMap) - mAccountStateMap->dropCache (); - } // returns false on error bool addSLE (SLE const& sle); diff --git a/src/ripple/app/ledger/LedgerCleaner.cpp b/src/ripple/app/ledger/LedgerCleaner.cpp index a4cd5518e..56d43b15a 100644 --- a/src/ripple/app/ledger/LedgerCleaner.cpp +++ b/src/ripple/app/ledger/LedgerCleaner.cpp @@ -305,8 +305,6 @@ public: return false; } - nodeLedger->dropCache(); - return true; } diff --git a/src/ripple/app/shamap/SHAMap.cpp b/src/ripple/app/shamap/SHAMap.cpp index 6a4924867..c38ec7f21 100644 --- a/src/ripple/app/shamap/SHAMap.cpp +++ b/src/ripple/app/shamap/SHAMap.cpp @@ -42,17 +42,13 @@ SHAMap::SHAMap ( , mTreeNodeCache (treeNodeCache) , mState (smsModifying) , mType (t) - , mTXMap (false) + , mBacked (true) , m_missing_node_handler (missing_node_handler) { assert (mSeq != 0); - if (t == smtSTATE) - mTNByID.rehash (STATE_MAP_BUCKETS); - SHAMapNodeID rootID{}; root = std::make_shared (mSeq); root->makeInner (); - mTNByID.replace(rootID, root); } SHAMap::SHAMap ( @@ -67,31 +63,17 @@ SHAMap::SHAMap ( , mTreeNodeCache (treeNodeCache) , mState (smsSynching) , mType (t) - , mTXMap (false) + , mBacked (true) , m_missing_node_handler (missing_node_handler) { - if (t == smtSTATE) - mTNByID.rehash (STATE_MAP_BUCKETS); - - SHAMapNodeID rootID{}; root = std::make_shared (mSeq); root->makeInner (); - mTNByID.replace(rootID, root); } SHAMap::~SHAMap () { mState = smsInvalid; - logTimedDestroy (mTNByID, "mTNByID with " + - std::to_string (mTNByID.size ()) + " items"); - - if (mDirtyNodes) - { - logTimedDestroy (mDirtyNodes, "mDirtyNodes with " + - std::to_string (mDirtyNodes->size ()) + " items"); - } - if (root) logTimedDestroy (root, "root node"); } @@ -102,48 +84,30 @@ SHAMap::pointer SHAMap::snapShot (bool isMutable) m_fullBelowCache, mTreeNodeCache); SHAMap& newMap = *ret; - // Return a new SHAMap that is a snapshot of this one - // Initially most nodes are shared and CoW is forced where needed + if (!isMutable) + newMap.mState = smsImmutable; + + newMap.mSeq = mSeq + 1; + newMap.root = root; + + if ((mState != smsImmutable) || !isMutable) { - ScopedReadLockType sl (mLock); - newMap.mSeq = mSeq; - newMap.mTNByID = mTNByID; - newMap.root = root; - - if (!isMutable) - newMap.mState = smsImmutable; - - // If the existing map has any nodes it might modify, unshare ours now - if (mState != smsImmutable) - { - for (NodeMap::value_type& nodeIt : mTNByID.peekMap()) - { - if (nodeIt.second->getSeq() == mSeq) - { // We might modify this node, so duplicate it in the snapShot - SHAMapTreeNode::pointer newNode = std::make_shared (*nodeIt.second, mSeq); - SHAMapNodeID const& newNodeID = nodeIt.first; - newMap.mTNByID.replace (newNodeID, newNode); - if (newNodeID.isRoot ()) - newMap.root = newNode; - } - } - } - else if (isMutable) // Need to unshare on changes to the snapshot - ++newMap.mSeq; + // If either map may change, they cannot share nodes + newMap.unshare (); } return ret; } -std::stack> +SHAMap::SharedPtrNodeStack SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) { // Walk the tree as far as possible to the specified identifier // produce a stack of nodes along the way, with the terminal node at the top - std::stack> stack; + SharedPtrNodeStack stack; + SHAMapTreeNode::pointer node = root; SHAMapNodeID nodeID; - uint256 nodeHash; while (!node->isLeaf ()) { @@ -152,10 +116,11 @@ SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) int branch = nodeID.selectBranch (id); assert (branch >= 0); - if (!node->descend (branch, nodeID, nodeHash)) + if (node->isEmptyBranch (branch)) return stack; - node = getNode (nodeID, nodeHash, false); + node = descendThrow (node, branch); + nodeID = nodeID.getChildNodeID (branch); } if (include_nonmatching_leaf || (node->peekItem ()->getTag () == id)) @@ -165,13 +130,16 @@ SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) } void -SHAMap::dirtyUp (std::stack>& stack, - uint256 const& target, uint256 prevHash) +SHAMap::dirtyUp (SharedPtrNodeStack& stack, + uint256 const& target, SHAMapTreeNode::pointer child) { // walk the tree up from through the inner nodes to the root - // update linking hashes and add nodes to dirty list + // update hashes and links + // stack is a path of inner nodes up to, but not including, child + // child can be an inner node or a leaf assert ((mState != smsSynching) && (mState != smsImmutable)); + assert (child && (child->getSeq() == mSeq)); while (!stack.empty ()) { @@ -183,9 +151,9 @@ SHAMap::dirtyUp (std::stack>& s int branch = nodeID.selectBranch (target); assert (branch >= 0); - returnNode (node, nodeID, true); + unshareNode (node, nodeID); - if (!node->setChildHash (branch, prevHash)) + if (! node->setChild (branch, child->getNodeHash(), child)) { WriteLog (lsFATAL, SHAMap) << "dirtyUp terminates early"; assert (false); @@ -195,200 +163,295 @@ SHAMap::dirtyUp (std::stack>& s #ifdef ST_DEBUG WriteLog (lsTRACE, SHAMap) << "dirtyUp sets branch " << branch << " to " << prevHash; #endif - prevHash = node->getNodeHash (); - assert (prevHash.isNonZero ()); + child = std::move (node); } } -SHAMapTreeNode::pointer SHAMap::checkCacheNode (const SHAMapNodeID& iNode) -{ - SHAMapTreeNode::pointer ret = mTNByID.retrieve(iNode); - if (ret && (ret->getSeq()!= 0)) - ret->touch (mSeq); - return ret; -} - -SHAMapTreeNode::pointer SHAMap::walkTo (uint256 const& id, bool modify) -{ - // walk down to the terminal node for this ID - - SHAMapTreeNode::pointer inNode = root; - SHAMapNodeID nodeID; - uint256 nodeHash; - - while (!inNode->isLeaf ()) - { - int branch = nodeID.selectBranch (id); - - if (!inNode->descend (branch, nodeID, nodeHash)) - return inNode; - - inNode = getNode (nodeID, nodeHash, false); - } - - if (inNode->getTag () != id) - return SHAMapTreeNode::pointer (); - - if (modify) - returnNode (inNode, nodeID, true); - - return inNode; -} - SHAMapTreeNode* SHAMap::walkToPointer (uint256 const& id) { SHAMapTreeNode* inNode = root.get (); SHAMapNodeID nodeID; uint256 nodeHash; - while (!inNode->isLeaf ()) + while (inNode->isInner ()) { int branch = nodeID.selectBranch (id); - if (!inNode->descend (branch, nodeID, nodeHash)) + if (inNode->isEmptyBranch (branch)) return nullptr; - inNode = getNodePointer (nodeID, nodeHash); - assert (inNode); + inNode = descendThrow (inNode, branch); + nodeID = nodeID.getChildNodeID (branch); } return (inNode->getTag () == id) ? inNode : nullptr; } -SHAMapTreeNode::pointer SHAMap::getNode (const SHAMapNodeID& id, uint256 const& hash, bool modify) +SHAMapTreeNode::pointer SHAMap::fetchNodeFromDB (uint256 const& hash) { - // retrieve a node whose node hash is known - SHAMapTreeNode::pointer node = checkCacheNode (id); + SHAMapTreeNode::pointer node; - if (node) + if (mBacked && getApp().running ()) { -#if BEAST_DEBUG - - if (node->getNodeHash () != hash) + NodeObject::pointer obj = getApp().getNodeStore().fetch (hash); + if (obj) { - WriteLog (lsFATAL, SHAMap) << "Attempt to get node, hash not in tree"; - WriteLog (lsFATAL, SHAMap) << "ID: " << id; - WriteLog (lsFATAL, SHAMap) << "TgtHash " << hash; - WriteLog (lsFATAL, SHAMap) << "NodHash " << node->getNodeHash (); - throw std::runtime_error ("invalid node"); + try + { + node = std::make_shared (obj->getData(), + 0, snfPREFIX, hash, true); + canonicalize (hash, node); + } + catch (...) + { + WriteLog (lsWARNING, SHAMap) << "Invalid DB node " << hash; + return SHAMapTreeNode::pointer (); + } } - -#endif - returnNode (node, id, modify); - return node; - } - - return fetchNodeExternal (id, hash); -} - -SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNodeID& id, uint256 const& hash) -{ - // fast, but you do not hold a reference - SHAMapTreeNode* ret = getNodePointerNT (id, hash); - - if (!ret) - throw (SHAMapMissingNode (mType, id, hash)); - - return ret; -} - -SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNodeID& id, uint256 const& hash) -{ - SHAMapTreeNode::pointer ret = mTNByID.retrieve (id); - if (!ret) - ret = fetchNodeExternalNT (id, hash); - return ret ? ret.get() : nullptr; -} - -SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNodeID& id, uint256 const& hash, SHAMapSyncFilter* filter) -{ - SHAMapTreeNode* ret = getNodePointerNT (id, hash, filter); - - if (!ret) - throw (SHAMapMissingNode (mType, id, hash)); - - return ret; -} - -SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNodeID& id, uint256 const& hash, SHAMapSyncFilter* filter) -{ - SHAMapTreeNode* node = getNodePointerNT (id, hash); - - if (!node && filter) - { // Our regular node store didn't have the node. See if the filter does - Blob nodeData; - - if (filter->haveNode (id, hash, nodeData)) + else if (mLedgerSeq != 0) { - SHAMapTreeNode::pointer node = std::make_shared ( - nodeData, 0, snfPREFIX, hash, true); - canonicalize (hash, node); - - // Canonicalize the node with mTNByID to make sure all threads gets the same node - // If the node is new, tell the filter - if (mTNByID.canonicalize (id, &node)) - filter->gotNode (true, id, hash, nodeData, node->getType ()); - - return node.get (); + m_missing_node_handler (mLedgerSeq); + mLedgerSeq = 0; } } return node; } +// See if a sync filter has a node +SHAMapTreeNode::pointer SHAMap::checkFilter ( + uint256 const& hash, + SHAMapNodeID const& id, + SHAMapSyncFilter* filter) +{ + SHAMapTreeNode::pointer node; + Blob nodeData; + + if (filter->haveNode (id, hash, nodeData)) + { + node = std::make_shared ( + nodeData, 0, snfPREFIX, hash, true); + + filter->gotNode (true, id, hash, nodeData, node->getType ()); + + if (mBacked) + canonicalize (hash, node); + } + + return node; +} + +// Get a node without throwing +// Used on maps where missing nodes are expected +SHAMapTreeNode::pointer SHAMap::fetchNodeNT( + SHAMapNodeID const& id, + uint256 const& hash, + SHAMapSyncFilter* filter) +{ + SHAMapTreeNode::pointer node = getCache (hash); + if (node) + return node; + + if (mBacked) + { + node = fetchNodeFromDB (hash); + if (node) + { + canonicalize (hash, node); + return node; + } + } + + if (filter) + node = checkFilter (hash, id, filter); + + return node; +} + +SHAMapTreeNode::pointer SHAMap::fetchNodeNT (uint256 const& hash) +{ + SHAMapTreeNode::pointer node = getCache (hash); + + if (!node && mBacked) + node = fetchNodeFromDB (hash); + + return node; +} + +// Throw if the node is missing +SHAMapTreeNode::pointer SHAMap::fetchNode (uint256 const& hash) +{ + SHAMapTreeNode::pointer node = fetchNodeNT (hash); + + if (!node) + throw SHAMapMissingNode (mType, hash); + + return node; +} + +SHAMapTreeNode* SHAMap::descendThrow (SHAMapTreeNode* parent, int branch) +{ + SHAMapTreeNode* ret = descend (parent, branch); + + if (! ret && ! parent->isEmptyBranch (branch)) + throw SHAMapMissingNode (mType, parent->getChildHash (branch)); + + return ret; +} + +SHAMapTreeNode::pointer SHAMap::descendThrow (SHAMapTreeNode::ref parent, int branch) +{ + SHAMapTreeNode::pointer ret = descend (parent, branch); + + if (! ret && ! parent->isEmptyBranch (branch)) + throw SHAMapMissingNode (mType, parent->getChildHash (branch)); + + return ret; +} + +SHAMapTreeNode* SHAMap::descend (SHAMapTreeNode* parent, int branch) +{ + SHAMapTreeNode* ret = parent->getChildPointer (branch); + if (ret || !mBacked) + return ret; + + SHAMapTreeNode::pointer node = fetchNodeNT (parent->getChildHash (branch)); + if (!node) + return nullptr; + + parent->canonicalizeChild (branch, node); + return node.get (); +} + +SHAMapTreeNode::pointer SHAMap::descend (SHAMapTreeNode::ref parent, int branch) +{ + SHAMapTreeNode::pointer node = parent->getChild (branch); + if (node || !mBacked) + return node; + + node = fetchNode (parent->getChildHash (branch)); + if (!node) + return nullptr; + + parent->canonicalizeChild (branch, node); + return node; +} + +// Gets the node that would be hooked to this branch, +// but doesn't hook it up. +SHAMapTreeNode::pointer SHAMap::descendNoStore (SHAMapTreeNode::ref parent, int branch) +{ + SHAMapTreeNode::pointer ret = parent->getChild (branch); + if (!ret && mBacked) + ret = fetchNode (parent->getChildHash (branch)); + return ret; +} + +std::pair +SHAMap::descend (SHAMapTreeNode * parent, SHAMapNodeID const& parentID, + int branch, SHAMapSyncFilter * filter) +{ + assert (parent->isInner ()); + assert ((branch >= 0) && (branch < 16)); + assert (!parent->isEmptyBranch (branch)); + + SHAMapNodeID childID = parentID.getChildNodeID (branch); + SHAMapTreeNode* child = parent->getChildPointer (branch); + uint256 const& childHash = parent->getChildHash (branch); + + if (!child) + { + SHAMapTreeNode::pointer childNode = fetchNodeNT (childID, childHash, filter); + + if (childNode) + { + parent->canonicalizeChild (branch, childNode); + child = childNode.get (); + } + } + + return std::make_pair (child, childID); +} + +SHAMapTreeNode* SHAMap::descendAsync (SHAMapTreeNode* parent, int branch, + SHAMapNodeID const& childID, SHAMapSyncFilter * filter, bool & pending) +{ + pending = false; + + SHAMapTreeNode* ret = parent->getChildPointer (branch); + if (ret) + return ret; + + uint256 const& hash = parent->getChildHash (branch); + + SHAMapTreeNode::pointer ptr = getCache (hash); + if (!ptr) + { + if (filter) + ptr = checkFilter (hash, childID, filter); + + if (!ptr && mBacked) + { + NodeObject::pointer obj; + if (!getApp().getNodeStore().asyncFetch (hash, obj)) + { + pending = true; + return nullptr; + } + if (!obj) + return nullptr; + + ptr = std::make_shared (obj->getData(), 0, snfPREFIX, hash, true); + + if (mBacked) + canonicalize (hash, ptr); + } + } + + if (ptr) + parent->canonicalizeChild (branch, ptr); + + return ptr.get (); +} void -SHAMap::returnNode (SHAMapTreeNode::pointer& node, SHAMapNodeID const& nodeID, - bool modify) +SHAMap::unshareNode (SHAMapTreeNode::pointer& node, SHAMapNodeID const& nodeID) { // make sure the node is suitable for the intended operation (copy on write) assert (node->isValid ()); assert (node->getSeq () <= mSeq); - if (node && modify && (node->getSeq () != mSeq)) + if (node->getSeq () != mSeq) { // have a CoW - assert (node->getSeq () < mSeq); assert (mState != smsImmutable); node = std::make_shared (*node, mSeq); // here's to the new node, same as the old node assert (node->isValid ()); - mTNByID.replace (nodeID, node); - if (nodeID.isRoot ()) root = node; - - if (mDirtyNodes) - mDirtyNodes->insert (nodeID); } } -void SHAMap::trackNewNode (SHAMapTreeNode::pointer& node, - SHAMapNodeID const& nodeID) -{ - assert (node->getSeq() == mSeq); - if (mDirtyNodes) - mDirtyNodes->insert (nodeID); -} - SHAMapTreeNode* -SHAMap::firstBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) +SHAMap::firstBelow (SHAMapTreeNode* node) { // Return the first item below this node do { assert(node != nullptr); - // Walk down the tree + if (node->hasItem ()) return node; + + // Walk down the tree bool foundNode = false; for (int i = 0; i < 16; ++i) { - uint256 nodeHash; - if (node->descend (i, nodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - node = getNodePointer (nodeID, nodeHash); + node = descendThrow (node, i); foundNode = true; break; } @@ -400,20 +463,20 @@ SHAMap::firstBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) } SHAMapTreeNode* -SHAMap::lastBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) +SHAMap::lastBelow (SHAMapTreeNode* node) { do { - // Walk down the tree if (node->hasItem ()) return node; + + // Walk down the tree bool foundNode = false; for (int i = 15; i >= 0; --i) { - uint256 nodeHash; - if (node->descend (i, nodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - node = getNodePointer (nodeID, nodeHash); + node = descendThrow (node, i); foundNode = true; break; } @@ -425,86 +488,45 @@ SHAMap::lastBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) } SHAMapItem::pointer -SHAMap::onlyBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) +SHAMap::onlyBelow (SHAMapTreeNode* node) { // If there is only one item below this node, return it + while (!node->isLeaf ()) { SHAMapTreeNode* nextNode = nullptr; - SHAMapNodeID nextNodeID; for (int i = 0; i < 16; ++i) { - SHAMapNodeID tempNodeID = nodeID; - uint256 nodeHash; - if (node->descend (i, tempNodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { if (nextNode) - return SHAMapItem::pointer (); // two leaves below - nextNode = getNodePointer (tempNodeID, nodeHash); - nextNodeID = tempNodeID; + return SHAMapItem::pointer (); + + nextNode = descendThrow (node, i); } } + if (!nextNode) { - WriteLog (lsFATAL, SHAMap) << nodeID; assert (false); return SHAMapItem::pointer (); } node = nextNode; - nodeID = nextNodeID; } - assert (node->hasItem ()); + // An inner node must have at least one leaf + // below it, unless it's the root + assert (node->hasItem () || (node == root.get ())); + return node->peekItem (); } -void -SHAMap::eraseChildren (SHAMapTreeNode::pointer node, SHAMapNodeID nodeID) -{ - // this node has only one item below it, erase its children - bool erase = false; - while (node->isInner ()) - { - for (int i = 0; i < 16; ++i) - { - uint256 nodeHash; - SHAMapNodeID nextNodeID = nodeID; - if (node->descend (i, nextNodeID, nodeHash)) - { - SHAMapTreeNode::pointer nextNode = getNode (nextNodeID, - nodeHash, false); - if (erase) - { - returnNode (node, nodeID, true); - - if (mTNByID.erase (nodeID)) - assert (false); - } - - erase = true; - node = nextNode; - nodeID = nextNodeID; - break; - } - } - } - - returnNode (node, nodeID, true); - - if (mTNByID.erase (nodeID) == 0) - assert (false); - - return; -} - static const SHAMapItem::pointer no_item; SHAMapItem::pointer SHAMap::peekFirstItem () { - ScopedReadLockType sl (mLock); - - SHAMapTreeNode* node = firstBelow (root.get (), SHAMapNodeID{}); + SHAMapTreeNode* node = firstBelow (root.get ()); if (!node) return no_item; @@ -514,9 +536,7 @@ SHAMapItem::pointer SHAMap::peekFirstItem () SHAMapItem::pointer SHAMap::peekFirstItem (SHAMapTreeNode::TNType& type) { - ScopedReadLockType sl (mLock); - - SHAMapTreeNode* node = firstBelow (root.get (), SHAMapNodeID{}); + SHAMapTreeNode* node = firstBelow (root.get ()); if (!node) return no_item; @@ -527,9 +547,7 @@ SHAMapItem::pointer SHAMap::peekFirstItem (SHAMapTreeNode::TNType& type) SHAMapItem::pointer SHAMap::peekLastItem () { - ScopedReadLockType sl (mLock); - - SHAMapTreeNode* node = lastBelow (root.get (), SHAMapNodeID{}); + SHAMapTreeNode* node = lastBelow (root.get ()); if (!node) return no_item; @@ -543,18 +561,16 @@ SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id) return peekNextItem (id, type); } - SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNType& type) { // Get a pointer to the next item in the tree after a given item - item need not be in tree - ScopedReadLockType sl (mLock); - std::stack> stack = - getStack (id, true); + auto stack = getStack (id, true); + while (!stack.empty ()) { - SHAMapTreeNode::pointer node = stack.top ().first; - SHAMapNodeID nodeID = stack.top ().second; + SHAMapTreeNode* node = stack.top().first.get(); + SHAMapNodeID nodeID = stack.top().second; stack.pop (); if (node->isLeaf ()) @@ -567,25 +583,19 @@ SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNT } else { - uint256 nodeHash; // breadth-first for (int i = nodeID.selectBranch (id) + 1; i < 16; ++i) - { - SHAMapNodeID childNodeID = nodeID; - if (node->descend (i, childNodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - SHAMapTreeNode* firstNode = getNodePointer ( - childNodeID, nodeHash); - assert (firstNode); - firstNode = firstBelow (firstNode, childNodeID); + node = descendThrow (node, i); + node = firstBelow (node); - if (!firstNode || firstNode->isInner ()) + if (!node || node->isInner ()) throw (std::runtime_error ("missing/corrupt node")); - type = firstNode->getType (); - return firstNode->peekItem (); + type = node->getType (); + return node->peekItem (); } - } } } @@ -596,13 +606,11 @@ SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNT // Get a pointer to the previous item in the tree after a given item - item need not be in tree SHAMapItem::pointer SHAMap::peekPrevItem (uint256 const& id) { - ScopedReadLockType sl (mLock); + auto stack = getStack (id, true); - std::stack> stack = - getStack (id, true); while (!stack.empty ()) { - SHAMapTreeNode::pointer node = stack.top ().first; + SHAMapTreeNode* node = stack.top ().first.get(); SHAMapNodeID nodeID = stack.top ().second; stack.pop (); @@ -613,31 +621,24 @@ SHAMapItem::pointer SHAMap::peekPrevItem (uint256 const& id) } else { - uint256 nodeHash; for (int i = nodeID.selectBranch (id) - 1; i >= 0; --i) { - if (node->descend (i, nodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - node = getNode (nodeID, nodeHash, false); - SHAMapTreeNode* item = firstBelow (node.get (), nodeID); - - if (!item) - throw (std::runtime_error ("missing node")); - - return item->peekItem (); + node = descendThrow (node, i); + node = lastBelow (node); + return node->peekItem (); } } } } - // must be last item + // must be first item return no_item; } SHAMapItem::pointer SHAMap::peekItem (uint256 const& id) { - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); if (!leaf) @@ -648,8 +649,6 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id) SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, SHAMapTreeNode::TNType& type) { - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); if (!leaf) @@ -661,8 +660,6 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, SHAMapTreeNode::TNType& SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, uint256& hash) { - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); if (!leaf) @@ -676,8 +673,6 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, uint256& hash) bool SHAMap::hasItem (uint256 const& id) { // does the tree have an item with this ID - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); return (leaf != nullptr); } @@ -685,11 +680,10 @@ bool SHAMap::hasItem (uint256 const& id) bool SHAMap::delItem (uint256 const& id) { // delete the item with this ID - ScopedWriteLockType sl (mLock); assert (mState != smsImmutable); - std::stack> stack = - getStack (id, true); + auto stack = getStack (id, true); + if (stack.empty ()) throw (std::runtime_error ("missing node")); @@ -701,21 +695,22 @@ bool SHAMap::delItem (uint256 const& id) return false; SHAMapTreeNode::TNType type = leaf->getType (); - returnNode (leaf, leafID, true); - - if (mTNByID.erase (leafID) == 0) - assert (false); + // What gets attached to the end of the chain + // (For now, nothing, since we deleted the leaf) uint256 prevHash; + SHAMapTreeNode::pointer prevNode; while (!stack.empty ()) { SHAMapTreeNode::pointer node = stack.top ().first; SHAMapNodeID nodeID = stack.top ().second; stack.pop (); - returnNode (node, nodeID, true); + assert (node->isInner ()); - if (!node->setChildHash (nodeID.selectBranch (id), prevHash)) + + unshareNode (node, nodeID); + if (! node->setChild (nodeID.selectBranch (id), prevHash, prevNode)) { assert (false); return true; @@ -724,37 +719,48 @@ bool SHAMap::delItem (uint256 const& id) if (!nodeID.isRoot ()) { // we may have made this a node with 1 or 0 children + // And, if so, we need to remove this branch int bc = node->getBranchCount (); if (bc == 0) { + // no children below this branch prevHash = uint256 (); - - if (!mTNByID.erase (nodeID)) - assert (false); + prevNode.reset (); } else if (bc == 1) { - // pull up on the thread - SHAMapItem::pointer item = onlyBelow (node.get (), nodeID); + // If there's only one item, pull up on the thread + SHAMapItem::pointer item = onlyBelow (node.get ()); if (item) { - returnNode (node, nodeID, true); - eraseChildren (node, nodeID); + for (int i = 0; i < 16; ++i) + { + if (!node->isEmptyBranch (i)) + { + if (! node->setChild (i, uint256(), nullptr)) + { + assert (false); + } + break; + } + } node->setItem (item, type); } prevHash = node->getNodeHash (); + prevNode = std::move (node); assert (prevHash.isNonZero ()); } else { + // This node is now the end of the branch prevHash = node->getNodeHash (); + prevNode = std::move (node); assert (prevHash.isNonZero ()); } } - else assert (stack.empty ()); } return true; @@ -765,13 +771,12 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta // add the specified item, does not update uint256 tag = item->getTag (); SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : - (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM); + (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM); - ScopedWriteLockType sl (mLock); assert (mState != smsImmutable); - std::stack> stack = - getStack (tag, true); + auto stack = getStack (tag, true); + if (stack.empty ()) throw (std::runtime_error ("missing node")); @@ -782,28 +787,18 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta if (node->isLeaf () && (node->peekItem ()->getTag () == tag)) return false; - uint256 prevHash; - returnNode (node, nodeID, true); + unshareNode (node, nodeID); if (node->isInner ()) { // easy case, we end on an inner node int branch = nodeID.selectBranch (tag); assert (node->isEmptyBranch (branch)); - SHAMapNodeID newNodeID = nodeID.getChildNodeID (branch); SHAMapTreeNode::pointer newNode = std::make_shared (item, type, mSeq); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) + if (! node->setChild (branch, newNode->getNodeHash (), newNode)) { - WriteLog (lsFATAL, SHAMap) << "Node: " << nodeID; - WriteLog (lsFATAL, SHAMap) << "NewNode: " << newNodeID; - dump (); assert (false); - throw (std::runtime_error ("invalid inner node")); } - - trackNewNode (newNode, newNodeID); - node->setChildHash (branch, newNode->getNodeHash ()); } else { @@ -818,45 +813,34 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta while ((b1 = nodeID.selectBranch (tag)) == (b2 = nodeID.selectBranch (otherItem->getTag ()))) { - // we need a new inner node, since both go on same branch at this level - SHAMapNodeID newNodeID = nodeID.getChildNodeID (b1); - SHAMapTreeNode::pointer newNode = - std::make_shared (mSeq); - newNode->makeInner (); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) - assert (false); - stack.push ({node, nodeID}); - node = newNode; - nodeID = newNodeID; - trackNewNode (node, nodeID); + + // we need a new inner node, since both go on same branch at this level + nodeID = nodeID.getChildNodeID (b1); + node = std::make_shared (mSeq); + node->makeInner (); } // we can add the two leaf nodes here assert (node->isInner ()); - SHAMapNodeID newNodeID = nodeID.getChildNodeID (b1); + SHAMapTreeNode::pointer newNode = std::make_shared (item, type, mSeq); assert (newNode->isValid () && newNode->isLeaf ()); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) + if (!node->setChild (b1, newNode->getNodeHash (), newNode)) + { assert (false); + } - node->setChildHash (b1, newNode->getNodeHash ()); // OPTIMIZEME hash op not needed - trackNewNode (newNode, newNodeID); - newNodeID = nodeID.getChildNodeID (b2); newNode = std::make_shared (otherItem, type, mSeq); assert (newNode->isValid () && newNode->isLeaf ()); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) + if (!node->setChild (b2, newNode->getNodeHash (), newNode)) + { assert (false); - - node->setChildHash (b2, newNode->getNodeHash ()); - trackNewNode (newNode, newNodeID); + } } - dirtyUp (stack, tag, node->getNodeHash ()); + dirtyUp (stack, tag, node); return true; } @@ -870,11 +854,10 @@ bool SHAMap::updateGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasM // can't change the tag but can change the hash uint256 tag = item->getTag (); - ScopedWriteLockType sl (mLock); assert (mState != smsImmutable); - std::stack> stack = - getStack (tag, true); + auto stack = getStack (tag, true); + if (stack.empty ()) throw (std::runtime_error ("missing node")); @@ -888,7 +871,7 @@ bool SHAMap::updateGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasM return false; } - returnNode (node, nodeID, true); + unshareNode (node, nodeID); if (!node->setItem (item, !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM))) @@ -897,7 +880,7 @@ bool SHAMap::updateGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasM return true; } - dirtyUp (stack, tag, node->getNodeHash ()); + dirtyUp (stack, tag, node); return true; } @@ -906,157 +889,6 @@ void SHAMapItem::dump () WriteLog (lsINFO, SHAMap) << "SHAMapItem(" << mTag << ") " << mData.size () << "bytes"; } -SHAMapTreeNode::pointer SHAMap::fetchNodeExternal (const SHAMapNodeID& id, uint256 const& hash) -{ - SHAMapTreeNode::pointer ret = fetchNodeExternalNT (id, hash); - - if (!ret) - throw (SHAMapMissingNode (mType, id, hash)); - - return ret; -} - -// Non-blocking version -SHAMapTreeNode* SHAMap::getNodeAsync ( - const SHAMapNodeID& id, - uint256 const& hash, - SHAMapSyncFilter *filter, - bool& pending) -{ - pending = false; - - // If the node is in mTNByID, return it - SHAMapTreeNode::pointer ptr = mTNByID.retrieve (id); - if (ptr) - return ptr.get (); - - // Try the tree node cache - ptr = getCache (hash); - - if (!ptr) - { - - // Try the filter - if (filter) - { - Blob nodeData; - if (filter->haveNode (id, hash, nodeData)) - { - ptr = std::make_shared ( - nodeData, 0, snfPREFIX, hash, true); - filter->gotNode (true, id, hash, nodeData, ptr->getType ()); - } - } - - if (!ptr) - { - if (mTXMap) - { - // We don't store proposed transaction nodes in the node store - return nullptr; - } - - NodeObject::pointer obj; - - if (!getApp().getNodeStore().asyncFetch (hash, obj)) - { // We would have to block - pending = true; - assert (!obj); - return nullptr; - } - - if (!obj) - return nullptr; - - ptr = std::make_shared (obj->getData(), 0, - snfPREFIX, hash, true); - } - - // Put it in the tree node cache - canonicalize (hash, ptr); - } - - if (id.isRoot ()) - { - // It is legal to replace the root - mTNByID.replace (id, ptr); - root = ptr; - } - else - mTNByID.canonicalize (id, &ptr); - - return ptr.get (); -} - -/** Look at the cache and back end (things external to this SHAMap) to - find a tree node. Only a read lock is required because mTNByID has its - own, internal synchronization. Every thread calling this function must - get a shared pointer to the same underlying node. - This function does not throw. -*/ -SHAMapTreeNode::pointer -SHAMap::fetchNodeExternalNT (const SHAMapNodeID& id, uint256 const& hash) -{ - SHAMapTreeNode::pointer ret; - - // This if allows us to use the SHAMap in unit tests. So we don't attempt - // to fetch external nodes if we're not running in the application. - if (!getApp().running ()) - return ret; - - // Check the cache of shared, immutable tree nodes - ret = getCache (hash); - if (ret) - { // The node was found in the TreeNodeCache - assert (ret->getSeq() == 0); - } - else - { // Check the back end - NodeObject::pointer obj (getApp ().getNodeStore ().fetch (hash)); - if (!obj) - { - if (mLedgerSeq != 0) - { - m_missing_node_handler (mLedgerSeq); - mLedgerSeq = 0; - } - - return ret; - } - - try - { - // We make this node immutable (seq == 0) so that it can be shared - // CoW is needed if it is modified - ret = std::make_shared (obj->getData (), 0, snfPREFIX, hash, true); - - if (ret->getNodeHash () != hash) - { - WriteLog (lsFATAL, SHAMap) << "Hashes don't match"; - assert (false); - return SHAMapTreeNode::pointer (); - } - - // Share this immutable tree node in the TreeNodeCache - canonicalize (hash, ret); - } - catch (...) - { - WriteLog (lsWARNING, SHAMap) << "fetchNodeExternal gets an invalid node: " << hash; - return SHAMapTreeNode::pointer (); - } - } - - if (id.isRoot ()) // it is legal to replace an existing root - { - mTNByID.replace(id, ret); - root = ret; - } - else // Make sure other threads get pointers to the same underlying object - mTNByID.canonicalize (id, &ret); - return ret; -} - bool SHAMap::fetchRoot (uint256 const& hash, SHAMapSyncFilter* filter) { if (hash == root->getNodeHash ()) @@ -1072,223 +904,247 @@ bool SHAMap::fetchRoot (uint256 const& hash, SHAMapSyncFilter* filter) WriteLog (lsTRACE, SHAMap) << "Fetch root SHAMap node " << hash; } - SHAMapTreeNode::pointer newRoot = fetchNodeExternalNT(SHAMapNodeID(), hash); + SHAMapTreeNode::pointer newRoot = fetchNodeNT (SHAMapNodeID(), hash, filter); if (newRoot) { root = newRoot; - } - else - { - Blob nodeData; - - if (!filter || !filter->haveNode (SHAMapNodeID (), hash, nodeData)) - return false; - - root = std::make_shared (nodeData, - mSeq - 1, snfPREFIX, hash, true); - filter->gotNode (true, SHAMapNodeID (), hash, nodeData, root->getType ()); + assert (root->getNodeHash () == hash); + return true; } - mTNByID.replace(SHAMapNodeID (), root); - - assert (root->getNodeHash () == hash); - return true; + return false; } -/** Begin saving dirty nodes to be written later */ -int SHAMap::armDirty () +// Replace a node with a shareable node. +// +// This code handles two cases: +// +// 1) An unshared, unshareable node needs to be made shareable +// so immutable SHAMap's can have references to it. +// +// 2) An unshareable node is shared. This happens when you make +// a mutable snapshot of a mutable SHAMap. +void SHAMap::writeNode ( + NodeObjectType t, std::uint32_t seq, SHAMapTreeNode::pointer& node) { - mDirtyNodes = std::make_shared (); - return ++mSeq; + // Node is ours, so we can just make it shareable + assert (node->getSeq() == mSeq); + assert (mBacked); + node->setSeq (0); + + canonicalize (node->getNodeHash(), node); + + Serializer s; + node->addRaw (s, snfPREFIX); + getApp().getNodeStore().store (t, seq, + std::move (s.modData ()), node->getNodeHash ()); } -/** Write all modified nodes to the node store */ -int -SHAMap::flushDirty (DirtySet& set, int maxNodes, NodeObjectType t, std::uint32_t seq) +// We can't modify an inner node someone else might have a +// pointer to because flushing modifies inner nodes -- it +// makes them point to canonical/shared nodes. +void SHAMap::preFlushNode (SHAMapTreeNode::pointer& node) +{ + // A shared node should never need to be flushed + // because that would imply someone modified it + assert (node->getSeq() != 0); + + if (node->getSeq() != mSeq) + { + // Node is not uniquely ours, so unshare it before + // possibly modifying it + node = std::make_shared (*node, mSeq); + } +} + +int SHAMap::unshare () +{ + return walkSubTree (false, hotUNKNOWN, 0); +} + +/** Convert all modified nodes to shared nodes */ +// If requested, write them to the node store +int SHAMap::flushDirty (NodeObjectType t, std::uint32_t seq) +{ + return walkSubTree (true, t, seq); +} + +int SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq) { int flushed = 0; Serializer s; - ScopedWriteLockType sl (mLock); + if (!root || (root->getSeq() == 0) || root->isEmpty ()) + return flushed; - for (DirtySet::iterator it = set.begin (); it != set.end (); it = set.erase (it)) - { - SHAMapNodeID nodeID = *it; - SHAMapTreeNode::pointer node = checkCacheNode (nodeID); - - // Check if node was deleted - if (!node) - continue; - - uint256 const nodeHash = node->getNodeHash(); - - s.erase (); - node->addRaw (s, snfPREFIX); - -#ifdef BEAST_DEBUG - - if (s.getSHA512Half () != nodeHash) - { - WriteLog (lsFATAL, SHAMap) << nodeID; - WriteLog (lsFATAL, SHAMap) << beast::lexicalCast (s.getDataLength ()); - WriteLog (lsFATAL, SHAMap) << s.getSHA512Half () << " != " << nodeHash; - assert (false); - } - -#endif - - if (node->getSeq () != 0) - { - // Node is not shareable - // Make and share a shareable copy - node = std::make_shared (*node, 0); - canonicalize (node->getNodeHash(), node); - mTNByID.replace (nodeID, node); - } - - getApp().getNodeStore ().store (t, seq, std::move (s.modData ()), nodeHash); - - if (flushed++ >= maxNodes) - return flushed; + if (root->isLeaf()) + { // special case -- root is leaf + preFlushNode (root); + if (doWrite && mBacked) + writeNode (t, seq, root); + return 1; } + // Stack of {parent,index,child} pointers representing + // inner nodes we are in the process of flushing + using StackEntry = std::pair ; + std::stack > stack; + + SHAMapTreeNode::pointer node = root; + preFlushNode (node); + + int pos = 0; + + // We can't flush an inner node until we flush its children + while (1) + { + while (pos < 16) + { + if (node->isEmptyBranch (pos)) + { + ++pos; + } + else + { + // No need to do I/O. If the node isn't linked, + // it can't need to be flushed + int branch = pos; + SHAMapTreeNode::pointer child = node->getChild (pos++); + + if (child && (child->getSeq() != 0)) + { + // This is a node that needs to be flushed + + if (child->isInner ()) + { + // save our place and work on this node + preFlushNode (child); + + stack.emplace (std::move (node), branch); + + node = std::move (child); + pos = 0; + } + else + { + // flush this leaf + ++flushed; + + preFlushNode (child); + + assert (node->getSeq() == mSeq); + + if (doWrite && mBacked) + writeNode (t, seq, child); + + node->shareChild (branch, child); + } + } + } + } + + // This inner node can now be shared + if (doWrite && mBacked) + writeNode (t, seq, node); + + ++flushed; + + if (stack.empty ()) + break; + + SHAMapTreeNode::pointer parent = std::move (stack.top().first); + pos = stack.top().second; + stack.pop(); + + // Hook this inner node to its parent + assert (parent->getSeq() == mSeq); + parent->shareChild (pos, node); + + // Continue with parent's next child, if any + node = std::move (parent); + ++pos; + } + + // Last inner node is the new root + root = std::move (node); + return flushed; } -/** Stop saving dirty nodes */ -std::shared_ptr SHAMap::disarmDirty () -{ - ScopedWriteLockType sl (mLock); - - std::shared_ptr ret; - ret.swap (mDirtyNodes); - return ret; -} - -SHAMapTreeNode::pointer SHAMap::getNode (const SHAMapNodeID& nodeID) -{ - - SHAMapTreeNode::pointer node = checkCacheNode (nodeID); - - if (node) - return node; - - node = root; - SHAMapNodeID currentID; - while (nodeID != currentID) - { - int branch = currentID.selectBranch (nodeID.getNodeID ()); - assert (branch >= 0); - uint256 currentHash; - if (!node->descend (branch, currentID, currentHash)) - return SHAMapTreeNode::pointer (); - node = getNode (currentID, currentHash, false); - assert (node); - } - - return node; -} - -// This function returns NULL if no node with that ID exists in the map -// It throws if the map is incomplete -SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNodeID& nodeID) -{ - SHAMapTreeNode::pointer nodeptr = mTNByID.retrieve (nodeID); - if (nodeptr) - { - SHAMapTreeNode* ret = nodeptr.get (); - ret->touch(mSeq); - return ret; - } - - SHAMapTreeNode* node = root.get(); - SHAMapNodeID currentID; - while (nodeID != currentID) - { - if (node->isLeaf ()) - return nullptr; - - int branch = currentID.selectBranch (nodeID.getNodeID ()); - assert (branch >= 0); - uint256 currentHash; - if (!node->descend (branch, currentID, currentHash)) - return nullptr; - node = getNodePointer (currentID, currentHash); - assert (node); - } - - return node; -} - bool SHAMap::getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeFormat format) { // Return the path of nodes to the specified index in the specified format // Return value: true = node present, false = node not present - ScopedReadLockType sl (mLock); - SHAMapTreeNode* inNode = root.get (); SHAMapNodeID nodeID; - while (!inNode->isLeaf ()) + + while (inNode->isInner ()) { Serializer s; inNode->addRaw (s, format); nodes.push_back (s.peekData ()); int branch = nodeID.selectBranch (index); - uint256 nodeHash; - if (!inNode->descend (branch, nodeID, nodeHash)) // paths leads to empty branch + if (inNode->isEmptyBranch (branch)) return false; - inNode = getNodePointer (nodeID, nodeHash); - assert (inNode); + inNode = descendThrow (inNode, branch); + nodeID = nodeID.getChildNodeID (branch); } if (inNode->getTag () != index) // path leads to different leaf return false; - // path lead to the requested leaf + // path leads to the requested leaf Serializer s; inNode->addRaw (s, format); - nodes.push_back (s.peekData ()); + nodes.push_back (std::move(s.peekData ())); return true; } -void SHAMap::dropCache () -{ - ScopedWriteLockType sl (mLock); - assert (mState == smsImmutable); - - mTNByID.clear (); - - if (root) - mTNByID.canonicalize(SHAMapNodeID{}, &root); -} - -void SHAMap::dropBelow (SHAMapTreeNode* d, SHAMapNodeID nodeID) -{ - if (d->isInner ()) - { - for (int i = 0 ; i < 16; ++i) - { - if (!d->isEmptyBranch (i)) - mTNByID.erase (nodeID.getChildNodeID (i)); - } - } -} - void SHAMap::dump (bool hash) { + int leafCount = 0; WriteLog (lsINFO, SHAMap) << " MAP Contains"; - ScopedWriteLockType sl (mLock); - for (auto const& p : mTNByID.peekMap()) + std::stack > stack; + stack.push ({root.get (), SHAMapNodeID ()}); + + do { - WriteLog (lsINFO, SHAMap) << p.second->getString (p.first); - CondLog (hash, lsINFO, SHAMap) << p.second->getNodeHash (); - } + SHAMapTreeNode* node = stack.top().first; + SHAMapNodeID nodeID = stack.top().second; + stack.pop(); + WriteLog (lsINFO, SHAMap) << node->getString (nodeID); + if (hash) + { + WriteLog (lsINFO, SHAMap) << "Hash: " << node->getNodeHash(); + } + + if (node->isInner ()) + { + for (int i = 0; i < 16; ++i) + { + if (!node->isEmptyBranch (i)) + { + SHAMapTreeNode* child = node->getChildPointer (i); + if (child) + { + assert (child->getNodeHash() == node->getChildHash (i)); + stack.push ({child, nodeID.getChildNodeID (i)}); + } + } + } + } + else + ++leafCount; + } + while (!stack.empty ()); + + WriteLog (lsINFO, SHAMap) << leafCount << " resident leaves"; } SHAMapTreeNode::pointer SHAMap::getCache (uint256 const& hash) @@ -1300,9 +1156,12 @@ SHAMapTreeNode::pointer SHAMap::getCache (uint256 const& hash) void SHAMap::canonicalize (uint256 const& hash, SHAMapTreeNode::pointer& node) { + assert (mBacked); assert (node->getSeq() == 0); + assert (node->getNodeHash() == hash); mTreeNodeCache.canonicalize (hash, node); + } //------------------------------------------------------------------------------ @@ -1362,7 +1221,9 @@ public: unexpected (i, "bad traverse"); sMap.addItem (i4, true, false); + sMap.delItem (i2.getTag ()); + sMap.addItem (i3, true, false); i = sMap.peekFirstItem (); diff --git a/src/ripple/app/shamap/SHAMap.h b/src/ripple/app/shamap/SHAMap.h index 5736548a0..4e8f7683f 100644 --- a/src/ripple/app/shamap/SHAMap.h +++ b/src/ripple/app/shamap/SHAMap.h @@ -114,11 +114,8 @@ public: typedef std::pair DeltaRef; typedef std::map Delta; typedef hash_map NodeMap; - typedef hash_set DirtySet; - typedef boost::shared_mutex LockType; - typedef boost::shared_lock ScopedReadLockType; - typedef boost::unique_lock ScopedWriteLockType; + typedef std::stack> SharedPtrNodeStack; public: // build new map @@ -138,17 +135,10 @@ public: ~SHAMap (); - std::size_t size () const noexcept - { - return mTNByID.size (); - } - - // Returns a new map that's a snapshot of this one. Force CoW + // Returns a new map that's a snapshot of this one. + // Handles copy on write for mutable snapshots. SHAMap::pointer snapShot (bool isMutable); - // Remove nodes from memory - void dropCache (); - void setLedgerSeq (std::uint32_t lseq) { mLedgerSeq = lseq; @@ -159,7 +149,7 @@ public: // normal hash access functions bool hasItem (uint256 const& id); bool delItem (uint256 const& id); - bool addItem (const SHAMapItem & i, bool isTransaction, bool hasMeta); + bool addItem (SHAMapItem const& i, bool isTransaction, bool hasMeta); uint256 getHash () const { @@ -182,7 +172,9 @@ public: SHAMapItem::pointer peekNextItem (uint256 const& ); SHAMapItem::pointer peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type); SHAMapItem::pointer peekPrevItem (uint256 const& ); - void visitLeaves(std::function); + + void visitNodes (std::function const&); + void visitLeaves(std::function const&); // comparison/sync functions void getMissingNodes (std::vector& nodeIDs, std::vector& hashes, int max, @@ -191,11 +183,11 @@ public: std::list& rawNode, bool fatRoot, bool fatLeaves); bool getRootNode (Serializer & s, SHANodeFormat format); std::vector getNeededHashes (int max, SHAMapSyncFilter * filter); - SHAMapAddNode addRootNode (uint256 const& hash, Blob const & rootNode, SHANodeFormat format, + SHAMapAddNode addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format, SHAMapSyncFilter * filter); - SHAMapAddNode addRootNode (Blob const & rootNode, SHANodeFormat format, + SHAMapAddNode addRootNode (Blob const& rootNode, SHANodeFormat format, SHAMapSyncFilter * filter); - SHAMapAddNode addKnownNode (const SHAMapNodeID & nodeID, Blob const & rawNode, + SHAMapAddNode addKnownNode (SHAMapNodeID const& nodeID, Blob const& rawNode, SHAMapSyncFilter * filter); // status functions @@ -225,10 +217,8 @@ public: // return value: true=successfully completed, false=too different bool compare (SHAMap::ref otherMap, Delta & differences, int maxCount); - int armDirty (); - int flushDirty (DirtySet & dirtySet, int maxNodes, NodeObjectType t, - std::uint32_t seq); - std::shared_ptr disarmDirty (); + int flushDirty (NodeObjectType t, std::uint32_t seq); + int unshare (); void walkMap (std::vector& missingNodes, int maxMissing); @@ -238,81 +228,105 @@ public: void getFetchPack (SHAMap * have, bool includeLeaves, int max, std::function); - void setTXMap () + void setUnbacked () { - mTXMap = true; + mBacked = false; } + void dump (bool withHashes = false); + private: // trusted path operations - prove a particular node is in a particular ledger std::list getTrustedPath (uint256 const& index); - SHAMapTreeNode::pointer fetchNodeExternal (const SHAMapNodeID & id, - uint256 const& hash); // throws - SHAMapTreeNode::pointer fetchNodeExternalNT (const SHAMapNodeID & id, - uint256 const& hash); // no throw - bool getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeFormat format); - void dump (bool withHashes = false); // tree node cache operations SHAMapTreeNode::pointer getCache (uint256 const& hash); void canonicalize (uint256 const& hash, SHAMapTreeNode::pointer&); - void dirtyUp (std::stack>& stack, - uint256 const& target, uint256 prevHash); - std::stack> + // database operations + SHAMapTreeNode::pointer fetchNodeFromDB (uint256 const& hash); + + SHAMapTreeNode::pointer fetchNodeNT (uint256 const& hash); + + SHAMapTreeNode::pointer fetchNodeNT ( + SHAMapNodeID const& id, + uint256 const& hash, + SHAMapSyncFilter *filter); + + SHAMapTreeNode::pointer fetchNode (uint256 const& hash); + + SHAMapTreeNode::pointer checkFilter (uint256 const& hash, SHAMapNodeID const& id, + SHAMapSyncFilter* filter); + + /** Update hashes up to the root */ + void dirtyUp (SharedPtrNodeStack& stack, + uint256 const& target, SHAMapTreeNode::pointer terminal); + + /** Get the path from the root to the specified node */ + SharedPtrNodeStack getStack (uint256 const& id, bool include_nonmatching_leaf); - SHAMapTreeNode::pointer walkTo (uint256 const& id, bool modify); + + /** Walk to the specified index, returning the node */ SHAMapTreeNode* walkToPointer (uint256 const& id); - SHAMapTreeNode::pointer checkCacheNode (const SHAMapNodeID&); - void returnNode (SHAMapTreeNode::pointer&, SHAMapNodeID const& nodeID, - bool modify); - void trackNewNode (SHAMapTreeNode::pointer&, SHAMapNodeID const&); - SHAMapTreeNode::pointer getNode (const SHAMapNodeID & id); - SHAMapTreeNode::pointer getNode (const SHAMapNodeID & id, uint256 const& hash, bool modify); - SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id); - SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id, uint256 const& hash); - SHAMapTreeNode* getNodePointerNT (const SHAMapNodeID & id, uint256 const& hash); - SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter); - SHAMapTreeNode* getNodePointerNT (const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter); - SHAMapTreeNode* firstBelow (SHAMapTreeNode*, SHAMapNodeID); - SHAMapTreeNode* lastBelow (SHAMapTreeNode*, SHAMapNodeID); + /** Unshare the node, allowing it to be modified */ + void unshareNode (SHAMapTreeNode::pointer&, SHAMapNodeID const& nodeID); - // Non-blocking version of getNodePointerNT - SHAMapTreeNode* getNodeAsync ( - const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter, bool& pending); + /** prepare a node to be modified before flushing */ + void preFlushNode (SHAMapTreeNode::pointer& node); - SHAMapItem::pointer onlyBelow (SHAMapTreeNode*, SHAMapNodeID); - void eraseChildren (SHAMapTreeNode::pointer, SHAMapNodeID); - void dropBelow (SHAMapTreeNode*, SHAMapNodeID); - bool hasInnerNode (const SHAMapNodeID & nodeID, uint256 const& hash); + /** write and canonicalize modified node */ + void writeNode (NodeObjectType t, std::uint32_t seq, + SHAMapTreeNode::pointer& node); + + SHAMapTreeNode* firstBelow (SHAMapTreeNode*); + SHAMapTreeNode* lastBelow (SHAMapTreeNode*); + + // Simple descent + // Get a child of the specified node + SHAMapTreeNode* descend (SHAMapTreeNode*, int branch); + SHAMapTreeNode* descendThrow (SHAMapTreeNode*, int branch); + SHAMapTreeNode::pointer descend (SHAMapTreeNode::ref, int branch); + SHAMapTreeNode::pointer descendThrow (SHAMapTreeNode::ref, int branch); + + // Descend with filter + SHAMapTreeNode* descendAsync (SHAMapTreeNode* parent, int branch, + SHAMapNodeID const& childID, SHAMapSyncFilter* filter, bool& pending); + + std::pair + descend (SHAMapTreeNode* parent, SHAMapNodeID const& parentID, + int branch, SHAMapSyncFilter* filter); + + // Non-storing + // Does not hook the returned node to its parent + SHAMapTreeNode::pointer descendNoStore (SHAMapTreeNode::ref, int branch); + + /** If there is only one leaf below this node, get its contents */ + SHAMapItem::pointer onlyBelow (SHAMapTreeNode*); + + bool hasInnerNode (SHAMapNodeID const& nodeID, uint256 const& hash); bool hasLeafNode (uint256 const& tag, uint256 const& hash); - bool walkBranch (SHAMapTreeNode* node, SHAMapNodeID nodeID, + bool walkBranch (SHAMapTreeNode* node, SHAMapItem::ref otherMapItem, bool isFirstMap, Delta & differences, int & maxCount); void visitLeavesInternal (std::function& function); -private: + int walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq); - // This lock protects key SHAMap structures. - // One may change anything with a write lock. - // With a read lock, one may not invalidate pointers to existing members of mTNByID - mutable LockType mLock; +private: FullBelowCache& m_fullBelowCache; std::uint32_t mSeq; std::uint32_t mLedgerSeq; // sequence number of ledger this is part of - SyncUnorderedMapType< SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash > mTNByID; - std::shared_ptr mDirtyNodes; TreeNodeCache& mTreeNodeCache; SHAMapTreeNode::pointer root; SHAMapState mState; SHAMapType mType; - bool mTXMap; // Map of transactions without metadata + bool mBacked; // Map is backed by the database MissingNodeHandler m_missing_node_handler; }; diff --git a/src/ripple/app/shamap/SHAMapDelta.cpp b/src/ripple/app/shamap/SHAMapDelta.cpp index b8ea00b65..05883b764 100644 --- a/src/ripple/app/shamap/SHAMapDelta.cpp +++ b/src/ripple/app/shamap/SHAMapDelta.cpp @@ -25,47 +25,29 @@ namespace ripple { // branches with the same branch hash. A limit can be passed so // that we will abort early if a node sends a map to us that // makes no sense at all. (And our sync algorithm will avoid -// synchronizing matching brances too.) +// synchronizing matching branches too.) -class SHAMapDeltaNode -{ -public: - SHAMapNodeID mNodeID; - uint256 mOurHash, mOtherHash; - - SHAMapDeltaNode (const SHAMapNodeID& id, uint256 const& ourHash, uint256 const& otherHash) : - mNodeID (id), mOurHash (ourHash), mOtherHash (otherHash) - { - ; - } -}; - -bool SHAMap::walkBranch (SHAMapTreeNode* node, SHAMapNodeID nodeID, +bool SHAMap::walkBranch (SHAMapTreeNode* node, SHAMapItem::ref otherMapItem, bool isFirstMap, Delta& differences, int& maxCount) { // Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map - std::stack> nodeStack; - nodeStack.push ({node, nodeID}); + std::stack > nodeStack; + nodeStack.push ({node}); bool emptyBranch = !otherMapItem; while (!nodeStack.empty ()) { - std::tie(node, nodeID) = nodeStack.top (); + node = nodeStack.top (); nodeStack.pop (); if (node->isInner ()) { // This is an inner node, add all non-empty branches - uint256 childNodeHash; for (int i = 0; i < 16; ++i) - { - SHAMapNodeID childNodeID = nodeID; - if (node->descend (i, childNodeID, childNodeHash)) - nodeStack.push ({getNodePointer (childNodeID, childNodeHash), - childNodeID}); - } + if (!node->isEmptyBranch (i)) + nodeStack.push ({descendThrow (node, i)}); } else { @@ -136,27 +118,23 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount) assert (isValid () && otherMap && otherMap->isValid ()); - std::stack nodeStack; // track nodes we've pushed - - ScopedReadLockType sl (mLock); + using StackEntry = std::pair ; + std::stack > nodeStack; // track nodes we've pushed if (getHash () == otherMap->getHash ()) return true; - nodeStack.push (SHAMapDeltaNode (SHAMapNodeID (), getHash (), - otherMap->getHash ())); + nodeStack.push ({root.get(), otherMap->root.get()}); while (!nodeStack.empty ()) { - SHAMapDeltaNode dNode (nodeStack.top ()); + SHAMapTreeNode* ourNode = nodeStack.top().first; + SHAMapTreeNode* otherNode = nodeStack.top().second; nodeStack.pop (); - SHAMapTreeNode* ourNode = getNodePointer (dNode.mNodeID, dNode.mOurHash); - SHAMapTreeNode* otherNode = otherMap->getNodePointer (dNode.mNodeID, - dNode.mOtherHash); if (!ourNode || !otherNode) { assert (false); - throw SHAMapMissingNode (mType, dNode.mNodeID, uint256 ()); + throw SHAMapMissingNode (mType, uint256 ()); } if (ourNode->isLeaf () && otherNode->isLeaf ()) @@ -190,14 +168,14 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount) } else if (ourNode->isInner () && otherNode->isLeaf ()) { - if (!walkBranch (ourNode, dNode.mNodeID, otherNode->peekItem (), - true, differences, maxCount)) + if (!walkBranch (ourNode, otherNode->peekItem (), + true, differences, maxCount)) return false; } else if (ourNode->isLeaf () && otherNode->isInner ()) { - if (!otherMap->walkBranch (otherNode, dNode.mNodeID, - ourNode->peekItem (), false, differences, maxCount)) + if (!otherMap->walkBranch (otherNode, ourNode->peekItem (), + false, differences, maxCount)) return false; } else if (ourNode->isInner () && otherNode->isInner ()) @@ -208,10 +186,8 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount) if (otherNode->isEmptyBranch (i)) { // We have a branch, the other tree does not - SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i); - SHAMapTreeNode* iNode = getNodePointer (childNodeID, - ourNode->getChildHash (i)); - if (!walkBranch (iNode, childNodeID, + SHAMapTreeNode* iNode = descendThrow (ourNode, i); + if (!walkBranch (iNode, SHAMapItem::pointer (), true, differences, maxCount)) return false; @@ -219,20 +195,16 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount) else if (ourNode->isEmptyBranch (i)) { // The other tree has a branch, we do not - SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i); SHAMapTreeNode* iNode = - otherMap->getNodePointer(childNodeID, - otherNode->getChildHash (i)); - if (!otherMap->walkBranch (iNode, childNodeID, + otherMap->descendThrow(otherNode, i); + if (!otherMap->walkBranch (iNode, SHAMapItem::pointer(), false, differences, maxCount)) return false; } else // The two trees have different non-empty branches - nodeStack.push (SHAMapDeltaNode ( - dNode.mNodeID.getChildNodeID (i), - ourNode->getChildHash (i), - otherNode->getChildHash (i))); + nodeStack.push ({descendThrow (ourNode, i), + otherMap->descendThrow (otherNode, i)}); } } else @@ -244,42 +216,37 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount) void SHAMap::walkMap (std::vector& missingNodes, int maxMissing) { - std::stack> nodeStack; - - ScopedReadLockType sl (mLock); + std::stack > nodeStack; if (!root->isInner ()) // root is only node, and we have it return; - nodeStack.push ({root, SHAMapNodeID{}}); + nodeStack.push (root); while (!nodeStack.empty ()) { - SHAMapTreeNode::pointer node; - SHAMapNodeID nodeID; - std::tie(node, nodeID) = nodeStack.top (); + SHAMapTreeNode::pointer node = std::move (nodeStack.top()); nodeStack.pop (); - uint256 childNodeHash; + for (int i = 0; i < 16; ++i) { - SHAMapNodeID childNodeID = nodeID; - if (node->descend (i, childNodeID, childNodeHash)) + if (!node->isEmptyBranch (i)) { - try - { - SHAMapTreeNode::pointer d = getNode(childNodeID, - childNodeHash, false); - if (d->isInner ()) - nodeStack.push ({d, childNodeID}); - } - catch (SHAMapMissingNode& n) - { - missingNodes.push_back (n); + SHAMapTreeNode::pointer nextNode = descendNoStore (node, i); - if (--maxMissing <= 0) - return; - } - } + if (nextNode) + { + if (nextNode->isInner ()) + nodeStack.push (std::move (nextNode)); + } + else + { + missingNodes.emplace_back (mType, node->getChildHash (i)); + if (--maxMissing <= 0) + return; + } + } } } } diff --git a/src/ripple/app/shamap/SHAMapMissingNode.cpp b/src/ripple/app/shamap/SHAMapMissingNode.cpp index 4a7c30bbc..c5d28b648 100644 --- a/src/ripple/app/shamap/SHAMapMissingNode.cpp +++ b/src/ripple/app/shamap/SHAMapMissingNode.cpp @@ -24,16 +24,16 @@ std::ostream& operator<< (std::ostream& out, const SHAMapMissingNode& mn) switch (mn.getMapType ()) { case smtTRANSACTION: - out << "Missing/TXN(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")"; + out << "Missing/TXN(" << mn.getNodeHash () << ")"; break; case smtSTATE: - out << "Missing/STA(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")"; + out << "Missing/STA(" << mn.getNodeHash () << ")"; break; case smtFREE: default: - out << "Missing/" << mn.getNodeID (); + out << "Missing/" << mn.getNodeHash (); break; }; diff --git a/src/ripple/app/shamap/SHAMapMissingNode.h b/src/ripple/app/shamap/SHAMapMissingNode.h index ab4398caa..8fefd1c1a 100644 --- a/src/ripple/app/shamap/SHAMapMissingNode.h +++ b/src/ripple/app/shamap/SHAMapMissingNode.h @@ -33,11 +33,9 @@ class SHAMapMissingNode : public std::runtime_error { public: SHAMapMissingNode (SHAMapType t, - SHAMapNodeID const& nodeID, uint256 const& nodeHash) : std::runtime_error ("SHAMapMissingNode") , mType (t) - , mNodeID (nodeID) , mNodeHash (nodeHash) { } @@ -51,11 +49,6 @@ public: return mType; } - SHAMapNodeID const& getNodeID () const - { - return mNodeID; - } - uint256 const& getNodeHash () const { return mNodeHash; @@ -63,7 +56,6 @@ public: private: SHAMapType mType; - SHAMapNodeID mNodeID; uint256 mNodeHash; }; diff --git a/src/ripple/app/shamap/SHAMapNodeID.cpp b/src/ripple/app/shamap/SHAMapNodeID.cpp index d6e725e32..8c90003ba 100644 --- a/src/ripple/app/shamap/SHAMapNodeID.cpp +++ b/src/ripple/app/shamap/SHAMapNodeID.cpp @@ -134,6 +134,7 @@ std::string SHAMapNodeID::getRawString () const SHAMapNodeID SHAMapNodeID::getChildNodeID (int m) const { assert ((m >= 0) && (m < 16)); + assert (mDepth <= 64); uint256 child (mNodeID); child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4); diff --git a/src/ripple/app/shamap/SHAMapSync.cpp b/src/ripple/app/shamap/SHAMapSync.cpp index 7bc1cdb4f..282443e30 100644 --- a/src/ripple/app/shamap/SHAMapSync.cpp +++ b/src/ripple/app/shamap/SHAMapSync.cpp @@ -26,46 +26,52 @@ namespace ripple { static const uint256 uZero; -void SHAMap::visitLeaves (std::function function) +static void visitLeavesHelper ( + std::function const& function, + SHAMapTreeNode& node) { - // Make a snapshot of this map so we don't need to hold - // a lock on the map we're visiting - snapShot (false)->visitLeavesInternal (function); + // Adapt visitNodes to visitLeaves + if (!node.isInner ()) + function (node.peekItem ()); } -void SHAMap::visitLeavesInternal (std::function& function) +void SHAMap::visitLeaves (std::function const& leafFunction) { + visitNodes (std::bind (visitLeavesHelper, + std::cref (leafFunction), std::placeholders::_1)); +} + +void SHAMap::visitNodes(std::function const& function) +{ + // Visit every node in a SHAMap assert (root->isValid ()); if (!root || root->isEmpty ()) return; - if (!root->isInner ()) - { - function (root->peekItem ()); - return; - } + function (*root); - std::stack> stack; - SHAMapTreeNode* node = root.get (); - SHAMapNodeID nodeID; + if (!root->isInner ()) + return; + + using StackEntry = std::pair ; + std::stack > stack; + + SHAMapTreeNode::pointer node = root; int pos = 0; while (1) { while (pos < 16) { - SHAMapNodeID childID = nodeID; uint256 childHash; - if (node->descend (pos, childID, childHash)) + if (!node->isEmptyBranch (pos)) { - SHAMapTreeNode* child = getNodePointer (childID, childHash); + SHAMapTreeNode::pointer child = descendNoStore (node, pos); + function (*child); + if (child->isLeaf ()) - { - function (child->peekItem ()); - mTNByID.erase (childID); // don't need this leaf anymore ++pos; - } else { // If there are no more children, don't push this node @@ -75,14 +81,11 @@ void SHAMap::visitLeavesInternal (std::function& fu if (pos != 15) { // save next position to resume at - stack.push (std::make_tuple(pos + 1, node, nodeID)); + stack.push (std::make_pair(pos + 1, std::move (node))); } - else - mTNByID.erase (nodeID); // don't need this inner node anymore // descend to the child's first position node = child; - nodeID = childID; pos = 0; } } @@ -92,13 +95,10 @@ void SHAMap::visitLeavesInternal (std::function& fu } } - // We are done with this inner node - mTNByID.erase (nodeID); - if (stack.empty ()) break; - std::tie(pos, node, nodeID) = stack.top (); + std::tie(pos, node) = stack.top (); stack.pop (); } } @@ -110,12 +110,9 @@ void SHAMap::visitLeavesInternal (std::function& fu void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vector& hashes, int max, SHAMapSyncFilter* filter) { - ScopedReadLockType sl (mLock); - assert (root->isValid ()); assert (root->getNodeHash().isNonZero ()); - if (root->isFullBelow ()) { clearSynching (); @@ -136,11 +133,12 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vector> deferredReads; + std::vector > deferredReads; deferredReads.reserve (maxDefer + 16); - std::stack > - stack; + using StackEntry = std::tuple; + std::stack > stack; + // Traverse the map without blocking SHAMapTreeNode *node = root.get (); @@ -164,11 +162,11 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vectorgetChildHash (branch); - if (! m_fullBelowCache.touch_if_exists (childHash)) + if (! mBacked || ! m_fullBelowCache.touch_if_exists (childHash)) { SHAMapNodeID childID = nodeID.getChildNodeID (branch); bool pending = false; - SHAMapTreeNode* d = getNodeAsync (childID, childHash, filter, pending); + SHAMapTreeNode* d = descendAsync (node, branch, childID, filter, pending); if (!d) { @@ -186,7 +184,7 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vector& nodeIDs, std::vectorsetFullBelow (); - if (mType == smtSTATE) + if (mBacked) m_fullBelowCache.insert (node->getNodeHash ()); } @@ -238,10 +236,19 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vector(node); + auto branch = std::get<1>(node); + auto const& nodeID = std::get<2>(node); + auto const& nodeHash = parent->getChildHash (branch); + + SHAMapTreeNode::pointer nodePtr = fetchNodeNT (nodeID, nodeHash, filter); + if (nodePtr) + { + if (mBacked) + canonicalize (nodeHash, nodePtr); + parent->canonicalizeChild (branch, nodePtr); + } + else if (missingHashes.insert (nodeHash).second) { nodeIDs.push_back (nodeID); hashes.push_back (nodeHash); @@ -273,11 +280,19 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector& nodeIDs std::list& rawNodes, bool fatRoot, bool fatLeaves) { // Gets a node and some of its children - ScopedReadLockType sl (mLock); - SHAMapTreeNode* node = getNodePointer(wanted); + SHAMapTreeNode* node = root.get (); - if (!node) + SHAMapNodeID nodeID; + + while (node && node->isInner () && (nodeID.getDepth() < wanted.getDepth())) + { + int branch = nodeID.selectBranch (wanted.getNodeID()); + node = descendThrow (node, branch); + nodeID = nodeID.getChildNodeID (branch); + } + + if (!node || (nodeID != wanted)) { WriteLog (lsWARNING, SHAMap) << "peer requested node that is not in the map: " << wanted; throw std::runtime_error ("Peer requested node not in map"); @@ -300,8 +315,8 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector& nodeIDs { Serializer s; node->addRaw (s, snfWIRE); - nodeIDs.push_back(wanted); - rawNodes.push_back (s.peekData ()); + nodeIDs.push_back (wanted); + rawNodes.push_back (std::move (s.peekData ())); } if ((!fatRoot && wanted.isRoot ()) || node->isLeaf ()) // don't get a fat root, can't get a fat leaf @@ -313,23 +328,22 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector& nodeIDs count = 0; for (int i = 0; i < 16; ++i) { - SHAMapNodeID tempNodeID = wanted; - uint256 nextNodeHash; - if (node->descend (i, tempNodeID, nextNodeHash)) + if (!node->isEmptyBranch (i)) { - nextNodeID = tempNodeID; - nextNode = getNodePointer (nextNodeID, nextNodeHash); + SHAMapNodeID nextNodeID = wanted.getChildNodeID (i); + nextNode = descendThrow (node, i); ++count; if (fatLeaves || nextNode->isInner ()) { Serializer s; nextNode->addRaw (s, snfWIRE); nodeIDs.push_back (nextNodeID); - rawNodes.push_back (s.peekData ()); + rawNodes.push_back (std::move (s.peekData ())); skipNode = true; // Don't add this node again if we loop } } } + node = nextNode; wanted = nextNodeID; @@ -341,7 +355,6 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector& nodeIDs bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format) { - ScopedReadLockType sl (mLock); root->addRaw (s, format); return true; } @@ -349,8 +362,6 @@ bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format) SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format, SHAMapSyncFilter* filter) { - ScopedWriteLockType sl (mLock); - // we already have a root node if (root->getNodeHash ().isNonZero ()) { @@ -360,7 +371,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format, assert (mSeq >= 1); SHAMapTreeNode::pointer node = - std::make_shared (rootNode, mSeq - 1, + std::make_shared (rootNode, 0, format, uZero, false); if (!node) @@ -370,8 +381,10 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format, node->dump (SHAMapNodeID ()); #endif + if (mBacked) + canonicalize (node->getNodeHash (), node); + root = node; - mTNByID.replace(SHAMapNodeID{}, root); if (root->isLeaf()) clearSynching (); @@ -390,8 +403,6 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format, SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format, SHAMapSyncFilter* filter) { - ScopedWriteLockType sl (mLock); - // we already have a root node if (root->getNodeHash ().isNonZero ()) { @@ -402,14 +413,16 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH assert (mSeq >= 1); SHAMapTreeNode::pointer node = - std::make_shared (rootNode, mSeq - 1, + std::make_shared (rootNode, 0, format, uZero, false); if (!node || node->getNodeHash () != hash) return SHAMapAddNode::invalid (); + if (mBacked) + canonicalize (hash, node); + root = node; - mTNByID.replace(SHAMapNodeID{}, root); if (root->isLeaf()) clearSynching (); @@ -429,8 +442,6 @@ SHAMapAddNode SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode, SHAMapSyncFilter* filter) { - ScopedWriteLockType sl (mLock); - // return value: true=okay, false=error assert (!node.isRoot ()); @@ -440,18 +451,10 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode, return SHAMapAddNode::duplicate (); } - if (checkCacheNode (node)) // Do we already have this node? - return SHAMapAddNode::duplicate (); + SHAMapNodeID iNodeID; + SHAMapTreeNode* iNode = root.get (); - SHAMapNodeID iNodeID = node.getParentNodeID(); - SHAMapTreeNode* iNode = checkCacheNode(iNodeID).get(); - if (iNode == nullptr) - { - iNode = root.get (); - iNodeID = SHAMapNodeID{}; - } - - while (!iNode->isLeaf () && !iNode->isFullBelow () && + while (iNode->isInner () && !iNode->isFullBelow () && (iNodeID.getDepth () < node.getDepth ())) { int branch = iNodeID.selectBranch (node.getNodeID ()); @@ -467,12 +470,13 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode, uint256 childHash = iNode->getChildHash (branch); if (m_fullBelowCache.touch_if_exists (childHash)) return SHAMapAddNode::duplicate (); - SHAMapNodeID nextNodeID = iNodeID.getChildNodeID (branch); - SHAMapTreeNode* nextNode = getNodePointerNT(nextNodeID, childHash, - filter); - if (!nextNode) + + SHAMapTreeNode* prevNode = iNode; + std::tie (iNode, iNodeID) = descend (iNode, iNodeID, branch, filter); + + if (!iNode) { - if (iNodeID.getDepth () != (node.getDepth () - 1)) + if (iNodeID != node) { // Either this node is broken or we didn't request it (yet) WriteLog (lsWARNING, SHAMap) << "unable to hook node " << node; @@ -487,22 +491,25 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode, std::make_shared (rawNode, 0, snfWIRE, uZero, false); - if (childHash != newNode->getNodeHash ()) - { - WriteLog (lsWARNING, SHAMap) << "Corrupt node received"; - return SHAMapAddNode::invalid (); - } - - canonicalize (childHash, newNode); - - if (!iNode->isInBounds (iNodeID)) + if (!newNode->isInBounds (iNodeID)) { // Map is provably invalid mState = smsInvalid; return SHAMapAddNode::useful (); } - if (mTNByID.canonicalize(node, &newNode) && filter) + if (childHash != newNode->getNodeHash ()) + { + WriteLog (lsWARNING, SHAMap) << "Corrupt node received"; + return SHAMapAddNode::invalid (); + } + + if (mBacked) + canonicalize (childHash, newNode); + + prevNode->canonicalizeChild (branch, newNode); + + if (filter) { Serializer s; newNode->addRaw (s, snfPREFIX); @@ -512,8 +519,6 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode, return SHAMapAddNode::useful (); } - iNode = nextNode; - iNodeID = nextNodeID; } WriteLog (lsTRACE, SHAMap) << "got node, already had it (late)"; @@ -523,41 +528,29 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode, bool SHAMap::deepCompare (SHAMap& other) { // Intended for debug/test only - std::stack> stack; - ScopedReadLockType sl (mLock); + std::stack > stack; - stack.push ({root, SHAMapNodeID{}}); + stack.push ({root.get(), other.root.get()}); while (!stack.empty ()) { - SHAMapTreeNode::pointer node; - SHAMapNodeID nodeID; - std::tie(node, nodeID) = stack.top (); + SHAMapTreeNode *node, *otherNode; + std::tie(node, otherNode) = stack.top (); stack.pop (); - SHAMapTreeNode::pointer otherNode; - - if (nodeID.isRoot ()) - otherNode = other.root; - else - otherNode = other.getNode (nodeID, node->getNodeHash (), false); - - if (!otherNode) + if (!node || !otherNode) { WriteLog (lsINFO, SHAMap) << "unable to fetch node"; return false; } else if (otherNode->getNodeHash () != node->getNodeHash ()) { - WriteLog (lsWARNING, SHAMap) << "node hash mismatch " << nodeID; + WriteLog (lsWARNING, SHAMap) << "node hash mismatch"; return false; } // WriteLog (lsTRACE) << "Comparing inner nodes " << *node; - if (node->getNodeHash () != otherNode->getNodeHash ()) - return false; - if (node->isLeaf ()) { if (!otherNode->isLeaf ()) @@ -583,16 +576,17 @@ bool SHAMap::deepCompare (SHAMap& other) } else { - SHAMapNodeID nextNodeID = nodeID; - uint256 nextNodeHash; - if (!node->descend (i, nextNodeID, nextNodeHash)) + if (otherNode->isEmptyBranch (i)) + return false; + + SHAMapTreeNode *next = descend (node, i); + SHAMapTreeNode *otherNext = other.descend (otherNode, i); + if (!next || !otherNext) { WriteLog (lsWARNING, SHAMap) << "unable to fetch inner node"; return false; } - SHAMapTreeNode::pointer next = getNode (nextNodeID, - nextNodeHash, false); - stack.push ({next, nextNodeID}); + stack.push ({next, otherNext}); } } } @@ -602,49 +596,50 @@ bool SHAMap::deepCompare (SHAMap& other) } /** Does this map have this inner node? - You must hold a read lock to call this function */ bool SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID, uint256 const& targetNodeHash) { - SHAMapTreeNode::pointer ptr = mTNByID.retrieve (targetNodeID); - if (ptr) - return ptr->getNodeHash() == targetNodeHash; - SHAMapTreeNode* node = root.get (); SHAMapNodeID nodeID; - uint256 nodeHash; + while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ())) { int branch = nodeID.selectBranch (targetNodeID.getNodeID ()); - if (!node->descend (branch, nodeID, nodeHash)) + + if (node->isEmptyBranch (branch)) return false; - node = getNodePointer (nodeID, nodeHash); + + node = descendThrow (node, branch); + nodeID = nodeID.getChildNodeID (branch); } - return nodeHash == targetNodeHash; + return (node->isInner()) && (node->getNodeHash() == targetNodeHash); } /** Does this map have this leaf node? - You must hold a read lock to call this function */ bool SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash) { SHAMapTreeNode* node = root.get (); SHAMapNodeID nodeID; - uint256 nodeHash; + if (!node->isInner()) // only one leaf node in the tree return node->getNodeHash() == targetNodeHash; do { int branch = nodeID.selectBranch (tag); - if (!node->descend (branch, nodeID, nodeHash)) + + if (node->isEmptyBranch (branch)) return false; // Dead end, node must not be here - if (nodeHash == targetNodeHash) // Matching leaf, no need to retrieve it + + if (node->getChildHash (branch) == targetNodeHash) // Matching leaf, no need to retrieve it return true; - node = getNodePointer (nodeID, nodeHash); + + node = descendThrow (node, branch); + nodeID = nodeID.getChildNodeID (branch); } while (node->isInner()); @@ -663,18 +658,6 @@ There's no point in including the leaves of transaction trees. void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max, std::function func) { - ScopedReadLockType ul1 (mLock, boost::defer_lock); - ScopedReadLockType ul2; - - if (have) - { - assert(this != have); - ul2 = ScopedReadLockType (have->mLock, boost::defer_lock); - std::lock(ul1, ul2); - } - else - ul1.lock(); - if (root->getNodeHash ().isZero ()) return; @@ -695,14 +678,16 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max, return; } // contains unexplored non-matching inner node entries - std::stack> stack; + using StackEntry = std::pair ; + std::stack > stack; + stack.push ({root.get(), SHAMapNodeID{}}); while (!stack.empty() && (max > 0)) { SHAMapTreeNode* node; SHAMapNodeID nodeID; - std::tie(node, nodeID) = stack.top (); + std::tie (node, nodeID) = stack.top (); stack.pop (); // 1) Add this node to the pack @@ -718,8 +703,7 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max, { uint256 const& childHash = node->getChildHash (i); SHAMapNodeID childID = nodeID.getChildNodeID (i); - - SHAMapTreeNode* next = getNodePointer (childID, childHash); + SHAMapTreeNode* next = descendThrow (node, i); if (next->isInner ()) { @@ -738,12 +722,9 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max, } } -std::list SHAMap::getTrustedPath (uint256 const& index) +std::list SHAMap::getTrustedPath (uint256 const& index) { - ScopedReadLockType sl (mLock); - - std::stack> stack = - getStack (index, false); + auto stack = getStack (index, false); if (stack.empty () || !stack.top ().first->isLeaf ()) throw std::runtime_error ("requested leaf not present"); @@ -796,6 +777,7 @@ public: { log << "Unable to add item to map"; + assert (false); return false; } } @@ -806,6 +788,7 @@ public: { log << "Unable to remove item from map"; + assert (false); return false; } } @@ -813,7 +796,8 @@ public: if (beforeHash != map.getHash ()) { log << - "Hashes do not match"; + "Hashes do not match " << beforeHash << " " << map.getHash (); + assert (false); return false; } diff --git a/src/ripple/app/shamap/SHAMapTreeNode.cpp b/src/ripple/app/shamap/SHAMapTreeNode.cpp index 01da8fe25..471b3e75d 100644 --- a/src/ripple/app/shamap/SHAMapTreeNode.cpp +++ b/src/ripple/app/shamap/SHAMapTreeNode.cpp @@ -21,10 +21,10 @@ namespace ripple { +std::mutex SHAMapTreeNode::childLock; + SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq) - : mHash (std::uint64_t(0)) - , mSeq (seq) - , mAccessSeq (seq) + : mSeq (seq) , mType (tnERROR) , mIsBranch (0) , mFullBelow (false) @@ -41,7 +41,14 @@ SHAMapTreeNode::SHAMapTreeNode (const SHAMapTreeNode& node, std::uint32_t seq) if (node.mItem) mItem = node.mItem; else + { memcpy (mHashes, node.mHashes, sizeof (mHashes)); + + std::unique_lock lock (childLock); + + for (int i = 0; i < 16; ++i) + mChildren[i] = node.mChildren[i]; + } } SHAMapTreeNode::SHAMapTreeNode (SHAMapItem::ref item, @@ -471,11 +478,13 @@ std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const return ret; } -bool SHAMapTreeNode::setChildHash (int m, uint256 const& hash) +// We are modifying an inner node +bool SHAMapTreeNode::setChild (int m, uint256 const& hash, SHAMapTreeNode::ref child) { assert ((m >= 0) && (m < 16)); assert (mType == tnINNER); assert (mSeq != 0); + assert (child.get() != this); if (mHashes[m] == hash) return false; @@ -483,31 +492,72 @@ bool SHAMapTreeNode::setChildHash (int m, uint256 const& hash) mHashes[m] = hash; if (hash.isNonZero ()) + { + assert (child && (child->getNodeHash() == hash)); mIsBranch |= (1 << m); + } else + { + assert (!child); mIsBranch &= ~ (1 << m); + } + + mChildren[m] = child; return updateHash (); } -// Descends along the specified branch -// On invocation, nodeID must be the ID of this node -// Returns false if there is no node down that branch -// Otherwise, returns true and fills in the node's ID and hash +// finished modifying, now make shareable +void SHAMapTreeNode::shareChild (int m, SHAMapTreeNode::ref child) +{ + assert ((m >= 0) && (m < 16)); + assert (mType == tnINNER); + assert (mSeq != 0); + assert (child); + assert (child.get() != this); + assert (child->getNodeHash() == mHashes[m]); -bool -SHAMapTreeNode::descend (int branch, SHAMapNodeID& nodeID, uint256& nodeHash) + mChildren[m] = child; +} + +SHAMapTreeNode* SHAMapTreeNode::getChildPointer (int branch) { assert (branch >= 0 && branch < 16); assert (isInnerNode ()); - if (isEmptyBranch (branch)) - return false; - - nodeID = nodeID.getChildNodeID (branch); - nodeHash = mHashes [branch]; - - return true; + std::unique_lock lock (childLock); + return mChildren[branch].get (); } +SHAMapTreeNode::pointer SHAMapTreeNode::getChild (int branch) +{ + assert (branch >= 0 && branch < 16); + assert (isInnerNode ()); + + std::unique_lock lock (childLock); + assert (!mChildren[branch] || (mHashes[branch] == mChildren[branch]->getNodeHash())); + return mChildren[branch]; +} + +void SHAMapTreeNode::canonicalizeChild (int branch, SHAMapTreeNode::pointer& node) +{ + assert (branch >= 0 && branch < 16); + assert (isInnerNode ()); + assert (node); + assert (node->getNodeHash() == mHashes[branch]); + + std::unique_lock lock (childLock); + if (mChildren[branch]) + { + // There is already a node hooked up, return it + node = mChildren[branch]; + } + else + { + // Hook this node up + mChildren[branch] = node; + } +} + + } // ripple diff --git a/src/ripple/app/shamap/SHAMapTreeNode.h b/src/ripple/app/shamap/SHAMapTreeNode.h index b3080dc5c..0d2241df3 100644 --- a/src/ripple/app/shamap/SHAMapTreeNode.h +++ b/src/ripple/app/shamap/SHAMapTreeNode.h @@ -77,12 +77,7 @@ public: } void setSeq (std::uint32_t s) { - mAccessSeq = mSeq = s; - } - void touch (std::uint32_t s) - { - if (mSeq != 0) - mAccessSeq = s; + mSeq = s; } uint256 const& getNodeHash () const { @@ -130,7 +125,13 @@ public: { return !mItem; } - bool setChildHash (int m, uint256 const& hash); + + // We are modifying the child hash + bool setChild (int m, uint256 const& hash, std::shared_ptr const& child); + + // We are sharing/unsharing the child + void shareChild (int m, std::shared_ptr const& child); + bool isEmptyBranch (int m) const { return (mIsBranch & (1 << m)) == 0; @@ -178,32 +179,27 @@ public: virtual void dump (SHAMapNodeID const&); virtual std::string getString (SHAMapNodeID const&) const; - /** Descend along the specified branch - On invocation, nodeID must be the ID of this node - Returns `false` if there is no node down that branch - Otherwise, returns `true` and fills in the node's ID and hash - - @param branch The branch to descend [0, 15] - @param nodeID On entry the ID of the parent. On exit the ID of the child - @param nodeHash On exit the hash of the child node. - @return `true` if nodeID and nodeHash are altered. - */ - bool descend (int branch, SHAMapNodeID& nodeID, uint256& nodeHash); + SHAMapTreeNode* getChildPointer (int branch); + SHAMapTreeNode::pointer getChild (int branch); + void canonicalizeChild (int branch, SHAMapTreeNode::pointer& node); private: // VFALCO TODO remove the use of friend friend class SHAMap; - uint256 mHash; - uint256 mHashes[16]; - SHAMapItem::pointer mItem; - std::uint32_t mSeq, mAccessSeq; - TNType mType; - int mIsBranch; - bool mFullBelow; + uint256 mHash; + uint256 mHashes[16]; + SHAMapTreeNode::pointer mChildren[16]; + SHAMapItem::pointer mItem; + std::uint32_t mSeq; + TNType mType; + int mIsBranch; + bool mFullBelow; bool updateHash (); + + static std::mutex childLock; }; using TreeNodeCache = TaggedCache ; diff --git a/src/ripple/app/tx/TransactionAcquire.cpp b/src/ripple/app/tx/TransactionAcquire.cpp index f7fa5b32a..23d72ae01 100644 --- a/src/ripple/app/tx/TransactionAcquire.cpp +++ b/src/ripple/app/tx/TransactionAcquire.cpp @@ -37,7 +37,7 @@ TransactionAcquire::TransactionAcquire (uint256 const& hash, clock_type& clock) Application& app = getApp(); mMap = std::make_shared (smtTRANSACTION, hash, app.getFullBelowCache (), app.getTreeNodeCache()); - mMap->setTXMap (); + mMap->setUnbacked (); } TransactionAcquire::~TransactionAcquire ()