mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
SHAMap performance improvements (RIPD-434)
This reworks the way SHAMaps are stored, flushed, backed, and traversed. Rather than storing the linkages in the SHAMap itself, that information is now stored in the nodes. This makes snapshotting much cheaper and also allows traverse work done on behalf of one SHAMap to be used by other SHAMaps that share inner nodes with that SHAMap. When a SHAMap is modified, nodes are modified all the way up to the root. This means that the modified nodes in a SHAMap can easily be traversed for flushing. So they don't need to be separately tracked. Summary * Remove mTNByID * Remove mDirtyNodes * Much faster traverses * Much Faster snapshots * New algorithm for flushing * New vistNodes/visitLeaves * Avoid I/O if a map is unbacked
This commit is contained in:
committed by
Tom Ritchford
parent
d26241de0e
commit
fc560179e0
@@ -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<SHAMap::DirtySet> acctNodes
|
||||
= newLCL->peekAccountStateMap ()->disarmDirty (); // stop tracking changes
|
||||
std::shared_ptr<SHAMap::DirtySet> 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);
|
||||
|
||||
@@ -1232,9 +1232,6 @@ Json::Value InboundLedger::getJson (int)
|
||||
{
|
||||
ret["have_state"] = mHaveState;
|
||||
ret["have_transactions"] = mHaveTransactions;
|
||||
if (!mHaveState)
|
||||
ret["state_nodes"] = static_cast<Json::Value::UInt>
|
||||
(mLedger->peekAccountStateMap()->size());
|
||||
}
|
||||
|
||||
if (mAborted)
|
||||
|
||||
@@ -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 <Ledger> (mTransactionMap,
|
||||
"mTransactionMap with " +
|
||||
std::to_string(mTransactionMap->size ()) + " items");
|
||||
logTimedDestroy <Ledger> (mTransactionMap, "mTransactionMap");
|
||||
}
|
||||
|
||||
if (mAccountStateMap)
|
||||
{
|
||||
logTimedDestroy <Ledger> (mAccountStateMap,
|
||||
"mAccountStateMap with " +
|
||||
std::to_string (mAccountStateMap->size ()) + " items");
|
||||
logTimedDestroy <Ledger> (mAccountStateMap, "mAccountStateMap");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -305,8 +305,6 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
nodeLedger->dropCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -114,11 +114,8 @@ public:
|
||||
typedef std::pair<SHAMapItem::ref, SHAMapItem::ref> DeltaRef;
|
||||
typedef std::map<uint256, DeltaItem> Delta;
|
||||
typedef hash_map<SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash> NodeMap;
|
||||
typedef hash_set<SHAMapNodeID, SHAMapNode_hash> DirtySet;
|
||||
|
||||
typedef boost::shared_mutex LockType;
|
||||
typedef boost::shared_lock<LockType> ScopedReadLockType;
|
||||
typedef boost::unique_lock<LockType> ScopedWriteLockType;
|
||||
typedef std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> 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 (SHAMapItem::ref)>);
|
||||
|
||||
void visitNodes (std::function<void (SHAMapTreeNode&)> const&);
|
||||
void visitLeaves(std::function<void (SHAMapItem::ref)> const&);
|
||||
|
||||
// comparison/sync functions
|
||||
void getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
|
||||
@@ -191,11 +183,11 @@ public:
|
||||
std::list<Blob >& rawNode, bool fatRoot, bool fatLeaves);
|
||||
bool getRootNode (Serializer & s, SHANodeFormat format);
|
||||
std::vector<uint256> 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<DirtySet> disarmDirty ();
|
||||
int flushDirty (NodeObjectType t, std::uint32_t seq);
|
||||
int unshare ();
|
||||
|
||||
void walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing);
|
||||
|
||||
@@ -238,81 +228,105 @@ public:
|
||||
|
||||
void getFetchPack (SHAMap * have, bool includeLeaves, int max, std::function<void (uint256 const&, const Blob&)>);
|
||||
|
||||
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<Blob > 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<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>& stack,
|
||||
uint256 const& target, uint256 prevHash);
|
||||
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>
|
||||
// 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 <SHAMapTreeNode*, SHAMapNodeID>
|
||||
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<void (SHAMapItem::ref item)>& 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<DirtySet> 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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<std::pair<SHAMapTreeNode*, SHAMapNodeID>> nodeStack;
|
||||
nodeStack.push ({node, nodeID});
|
||||
std::stack <SHAMapTreeNode*, std::vector<SHAMapTreeNode*>> 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<SHAMapDeltaNode> nodeStack; // track nodes we've pushed
|
||||
|
||||
ScopedReadLockType sl (mLock);
|
||||
using StackEntry = std::pair <SHAMapTreeNode*, SHAMapTreeNode*>;
|
||||
std::stack <StackEntry, std::vector<StackEntry>> 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<SHAMapMissingNode>& missingNodes, int maxMissing)
|
||||
{
|
||||
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> nodeStack;
|
||||
|
||||
ScopedReadLockType sl (mLock);
|
||||
std::stack <SHAMapTreeNode::pointer,
|
||||
std::vector <SHAMapTreeNode::pointer>> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -26,46 +26,52 @@ namespace ripple {
|
||||
|
||||
static const uint256 uZero;
|
||||
|
||||
void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> function)
|
||||
static void visitLeavesHelper (
|
||||
std::function <void (SHAMapItem::ref)> 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<void (SHAMapItem::ref item)>& function)
|
||||
void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> const& leafFunction)
|
||||
{
|
||||
visitNodes (std::bind (visitLeavesHelper,
|
||||
std::cref (leafFunction), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SHAMap::visitNodes(std::function<void (SHAMapTreeNode&)> 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<std::tuple<int, SHAMapTreeNode*, SHAMapNodeID>> stack;
|
||||
SHAMapTreeNode* node = root.get ();
|
||||
SHAMapNodeID nodeID;
|
||||
if (!root->isInner ())
|
||||
return;
|
||||
|
||||
using StackEntry = std::pair <int, SHAMapTreeNode::pointer>;
|
||||
std::stack <StackEntry, std::vector <StackEntry>> 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<void (SHAMapItem::ref item)>& 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<void (SHAMapItem::ref item)>& 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<void (SHAMapItem::ref item)>& fu
|
||||
void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& 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<SHAMapNodeID>& nodeIDs, std::vector<ui
|
||||
|
||||
while (1)
|
||||
{
|
||||
std::vector <std::pair <SHAMapNodeID, uint256>> deferredReads;
|
||||
std::vector <std::tuple <SHAMapTreeNode*, int, SHAMapNodeID>> deferredReads;
|
||||
deferredReads.reserve (maxDefer + 16);
|
||||
|
||||
std::stack <std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>>
|
||||
stack;
|
||||
using StackEntry = std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>;
|
||||
std::stack <StackEntry, std::vector<StackEntry>> stack;
|
||||
|
||||
// Traverse the map without blocking
|
||||
|
||||
SHAMapTreeNode *node = root.get ();
|
||||
@@ -164,11 +162,11 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
|
||||
{
|
||||
uint256 const& childHash = node->getChildHash (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<SHAMapNodeID>& nodeIDs, std::vector<ui
|
||||
else
|
||||
{
|
||||
// read is deferred
|
||||
deferredReads.emplace_back (childID, childHash);
|
||||
deferredReads.emplace_back (node, branch, childID);
|
||||
}
|
||||
|
||||
fullBelow = false; // This node is not known full below
|
||||
@@ -212,7 +210,7 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
|
||||
if (fullBelow)
|
||||
{ // No partial node encountered below this node
|
||||
node->setFullBelow ();
|
||||
if (mType == smtSTATE)
|
||||
if (mBacked)
|
||||
m_fullBelowCache.insert (node->getNodeHash ());
|
||||
}
|
||||
|
||||
@@ -238,10 +236,19 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
|
||||
// Process all deferred reads
|
||||
for (auto const& node : deferredReads)
|
||||
{
|
||||
auto const& nodeID = node.first;
|
||||
auto const& nodeHash = node.second;
|
||||
SHAMapTreeNode* nodePtr = getNodePointerNT (nodeID, nodeHash, filter);
|
||||
if (!nodePtr && missingHashes.insert (nodeHash).second)
|
||||
auto parent = std::get<0>(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<SHAMapNodeID>& nodeIDs
|
||||
std::list<Blob >& 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<SHAMapNodeID>& 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<SHAMapNodeID>& 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<SHAMapNodeID>& 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<SHAMapTreeNode> (rootNode, mSeq - 1,
|
||||
std::make_shared<SHAMapTreeNode> (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<SHAMapTreeNode> (rootNode, mSeq - 1,
|
||||
std::make_shared<SHAMapTreeNode> (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<SHAMapTreeNode> (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<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> stack;
|
||||
ScopedReadLockType sl (mLock);
|
||||
std::stack <std::pair <SHAMapTreeNode*, SHAMapTreeNode*> > 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<void (uint256 const&, const Blob&)> 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<std::pair<SHAMapTreeNode*, SHAMapNodeID>> stack;
|
||||
using StackEntry = std::pair <SHAMapTreeNode*, SHAMapNodeID>;
|
||||
std::stack <StackEntry, std::vector<StackEntry>> 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<Blob > SHAMap::getTrustedPath (uint256 const& index)
|
||||
std::list <Blob> SHAMap::getTrustedPath (uint256 const& index)
|
||||
{
|
||||
ScopedReadLockType sl (mLock);
|
||||
|
||||
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <std::mutex> 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 <std::mutex> lock (childLock);
|
||||
return mChildren[branch].get ();
|
||||
}
|
||||
|
||||
SHAMapTreeNode::pointer SHAMapTreeNode::getChild (int branch)
|
||||
{
|
||||
assert (branch >= 0 && branch < 16);
|
||||
assert (isInnerNode ());
|
||||
|
||||
std::unique_lock <std::mutex> 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 <std::mutex> 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
|
||||
|
||||
@@ -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<SHAMapTreeNode> const& child);
|
||||
|
||||
// We are sharing/unsharing the child
|
||||
void shareChild (int m, std::shared_ptr<SHAMapTreeNode> 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 <uint256, SHAMapTreeNode>;
|
||||
|
||||
@@ -37,7 +37,7 @@ TransactionAcquire::TransactionAcquire (uint256 const& hash, clock_type& clock)
|
||||
Application& app = getApp();
|
||||
mMap = std::make_shared<SHAMap> (smtTRANSACTION, hash,
|
||||
app.getFullBelowCache (), app.getTreeNodeCache());
|
||||
mMap->setTXMap ();
|
||||
mMap->setUnbacked ();
|
||||
}
|
||||
|
||||
TransactionAcquire::~TransactionAcquire ()
|
||||
|
||||
Reference in New Issue
Block a user