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,
|
// Set up to write SHAMap changes to our database,
|
||||||
// perform updates, extract changes
|
// perform updates, extract changes
|
||||||
newLCL->peekTransactionMap ()->armDirty (); // start tracking changes
|
|
||||||
newLCL->peekAccountStateMap ()->armDirty ();
|
|
||||||
WriteLog (lsDEBUG, LedgerConsensus)
|
WriteLog (lsDEBUG, LedgerConsensus)
|
||||||
<< "Applying consensus set transactions to the"
|
<< "Applying consensus set transactions to the"
|
||||||
<< " last closed ledger";
|
<< " last closed ledger";
|
||||||
applyTransactions (set, newLCL, newLCL, retriableTransactions, false);
|
applyTransactions (set, newLCL, newLCL, retriableTransactions, false);
|
||||||
newLCL->updateSkipList ();
|
newLCL->updateSkipList ();
|
||||||
newLCL->setClosed ();
|
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 asf = newLCL->peekAccountStateMap ()->flushDirty (
|
||||||
int fc;
|
hotACCOUNT_NODE, newLCL->getLedgerSeq());
|
||||||
|
int tmf = newLCL->peekTransactionMap ()->flushDirty (
|
||||||
while ((fc = newLCL->peekAccountStateMap()->flushDirty (
|
hotTRANSACTION_NODE, newLCL->getLedgerSeq());
|
||||||
*acctNodes, 256, hotACCOUNT_NODE, newLCL->getLedgerSeq ())) > 0)
|
WriteLog (lsDEBUG, LedgerConsensus) << "Flushed " << asf << " account and " <<
|
||||||
{
|
tmf << "transaction nodes";
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept ledger
|
// Accept ledger
|
||||||
newLCL->setAccepted (closeTime, mCloseResolution, closeTimeCorrect);
|
newLCL->setAccepted (closeTime, mCloseResolution, closeTimeCorrect);
|
||||||
|
|||||||
@@ -1232,9 +1232,6 @@ Json::Value InboundLedger::getJson (int)
|
|||||||
{
|
{
|
||||||
ret["have_state"] = mHaveState;
|
ret["have_state"] = mHaveState;
|
||||||
ret["have_transactions"] = mHaveTransactions;
|
ret["have_transactions"] = mHaveTransactions;
|
||||||
if (!mHaveState)
|
|
||||||
ret["state_nodes"] = static_cast<Json::Value::UInt>
|
|
||||||
(mLedger->peekAccountStateMap()->size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAborted)
|
if (mAborted)
|
||||||
|
|||||||
@@ -57,14 +57,9 @@ Ledger::Ledger (RippleAddress const& masterID, std::uint64_t startAmount)
|
|||||||
WriteLog (lsTRACE, Ledger)
|
WriteLog (lsTRACE, Ledger)
|
||||||
<< "root account: " << startAccount->peekSLE ().getJson (0);
|
<< "root account: " << startAccount->peekSLE ().getJson (0);
|
||||||
|
|
||||||
mAccountStateMap->armDirty ();
|
|
||||||
|
|
||||||
writeBack (lepCREATE, startAccount->getSLE ());
|
writeBack (lepCREATE, startAccount->getSLE ());
|
||||||
|
|
||||||
auto dirtyNodes = mAccountStateMap->disarmDirty();
|
mAccountStateMap->flushDirty (hotACCOUNT_NODE, mLedgerSeq);
|
||||||
mAccountStateMap->flushDirty (
|
|
||||||
*dirtyNodes, 256, hotACCOUNT_NODE, mLedgerSeq);
|
|
||||||
// TODO(tom): why 256?
|
|
||||||
|
|
||||||
initializeFees ();
|
initializeFees ();
|
||||||
}
|
}
|
||||||
@@ -243,16 +238,12 @@ Ledger::~Ledger ()
|
|||||||
{
|
{
|
||||||
if (mTransactionMap)
|
if (mTransactionMap)
|
||||||
{
|
{
|
||||||
logTimedDestroy <Ledger> (mTransactionMap,
|
logTimedDestroy <Ledger> (mTransactionMap, "mTransactionMap");
|
||||||
"mTransactionMap with " +
|
|
||||||
std::to_string(mTransactionMap->size ()) + " items");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAccountStateMap)
|
if (mAccountStateMap)
|
||||||
{
|
{
|
||||||
logTimedDestroy <Ledger> (mAccountStateMap,
|
logTimedDestroy <Ledger> (mAccountStateMap, "mAccountStateMap");
|
||||||
"mAccountStateMap with " +
|
|
||||||
std::to_string (mAccountStateMap->size ()) + " items");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -239,16 +239,6 @@ public:
|
|||||||
{
|
{
|
||||||
return mAccountStateMap;
|
return mAccountStateMap;
|
||||||
}
|
}
|
||||||
void dropCache ()
|
|
||||||
{
|
|
||||||
assert (isImmutable ());
|
|
||||||
|
|
||||||
if (mTransactionMap)
|
|
||||||
mTransactionMap->dropCache ();
|
|
||||||
|
|
||||||
if (mAccountStateMap)
|
|
||||||
mAccountStateMap->dropCache ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns false on error
|
// returns false on error
|
||||||
bool addSLE (SLE const& sle);
|
bool addSLE (SLE const& sle);
|
||||||
|
|||||||
@@ -305,8 +305,6 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeLedger->dropCache();
|
|
||||||
|
|
||||||
return true;
|
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::pair<SHAMapItem::ref, SHAMapItem::ref> DeltaRef;
|
||||||
typedef std::map<uint256, DeltaItem> Delta;
|
typedef std::map<uint256, DeltaItem> Delta;
|
||||||
typedef hash_map<SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash> NodeMap;
|
typedef hash_map<SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash> NodeMap;
|
||||||
typedef hash_set<SHAMapNodeID, SHAMapNode_hash> DirtySet;
|
|
||||||
|
|
||||||
typedef boost::shared_mutex LockType;
|
typedef std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> SharedPtrNodeStack;
|
||||||
typedef boost::shared_lock<LockType> ScopedReadLockType;
|
|
||||||
typedef boost::unique_lock<LockType> ScopedWriteLockType;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// build new map
|
// build new map
|
||||||
@@ -138,17 +135,10 @@ public:
|
|||||||
|
|
||||||
~SHAMap ();
|
~SHAMap ();
|
||||||
|
|
||||||
std::size_t size () const noexcept
|
// Returns a new map that's a snapshot of this one.
|
||||||
{
|
// Handles copy on write for mutable snapshots.
|
||||||
return mTNByID.size ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a new map that's a snapshot of this one. Force CoW
|
|
||||||
SHAMap::pointer snapShot (bool isMutable);
|
SHAMap::pointer snapShot (bool isMutable);
|
||||||
|
|
||||||
// Remove nodes from memory
|
|
||||||
void dropCache ();
|
|
||||||
|
|
||||||
void setLedgerSeq (std::uint32_t lseq)
|
void setLedgerSeq (std::uint32_t lseq)
|
||||||
{
|
{
|
||||||
mLedgerSeq = lseq;
|
mLedgerSeq = lseq;
|
||||||
@@ -159,7 +149,7 @@ public:
|
|||||||
// normal hash access functions
|
// normal hash access functions
|
||||||
bool hasItem (uint256 const& id);
|
bool hasItem (uint256 const& id);
|
||||||
bool delItem (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
|
uint256 getHash () const
|
||||||
{
|
{
|
||||||
@@ -182,7 +172,9 @@ public:
|
|||||||
SHAMapItem::pointer peekNextItem (uint256 const& );
|
SHAMapItem::pointer peekNextItem (uint256 const& );
|
||||||
SHAMapItem::pointer peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type);
|
SHAMapItem::pointer peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type);
|
||||||
SHAMapItem::pointer peekPrevItem (uint256 const& );
|
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
|
// comparison/sync functions
|
||||||
void getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
|
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);
|
std::list<Blob >& rawNode, bool fatRoot, bool fatLeaves);
|
||||||
bool getRootNode (Serializer & s, SHANodeFormat format);
|
bool getRootNode (Serializer & s, SHANodeFormat format);
|
||||||
std::vector<uint256> getNeededHashes (int max, SHAMapSyncFilter * filter);
|
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);
|
SHAMapSyncFilter * filter);
|
||||||
SHAMapAddNode addRootNode (Blob const & rootNode, SHANodeFormat format,
|
SHAMapAddNode addRootNode (Blob const& rootNode, SHANodeFormat format,
|
||||||
SHAMapSyncFilter * filter);
|
SHAMapSyncFilter * filter);
|
||||||
SHAMapAddNode addKnownNode (const SHAMapNodeID & nodeID, Blob const & rawNode,
|
SHAMapAddNode addKnownNode (SHAMapNodeID const& nodeID, Blob const& rawNode,
|
||||||
SHAMapSyncFilter * filter);
|
SHAMapSyncFilter * filter);
|
||||||
|
|
||||||
// status functions
|
// status functions
|
||||||
@@ -225,10 +217,8 @@ public:
|
|||||||
// return value: true=successfully completed, false=too different
|
// return value: true=successfully completed, false=too different
|
||||||
bool compare (SHAMap::ref otherMap, Delta & differences, int maxCount);
|
bool compare (SHAMap::ref otherMap, Delta & differences, int maxCount);
|
||||||
|
|
||||||
int armDirty ();
|
int flushDirty (NodeObjectType t, std::uint32_t seq);
|
||||||
int flushDirty (DirtySet & dirtySet, int maxNodes, NodeObjectType t,
|
int unshare ();
|
||||||
std::uint32_t seq);
|
|
||||||
std::shared_ptr<DirtySet> disarmDirty ();
|
|
||||||
|
|
||||||
void walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing);
|
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 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:
|
private:
|
||||||
// trusted path operations - prove a particular node is in a particular ledger
|
// trusted path operations - prove a particular node is in a particular ledger
|
||||||
std::list<Blob > getTrustedPath (uint256 const& index);
|
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);
|
bool getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeFormat format);
|
||||||
void dump (bool withHashes = false);
|
|
||||||
|
|
||||||
// tree node cache operations
|
// tree node cache operations
|
||||||
SHAMapTreeNode::pointer getCache (uint256 const& hash);
|
SHAMapTreeNode::pointer getCache (uint256 const& hash);
|
||||||
void canonicalize (uint256 const& hash, SHAMapTreeNode::pointer&);
|
void canonicalize (uint256 const& hash, SHAMapTreeNode::pointer&);
|
||||||
|
|
||||||
void dirtyUp (std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>& stack,
|
// database operations
|
||||||
uint256 const& target, uint256 prevHash);
|
SHAMapTreeNode::pointer fetchNodeFromDB (uint256 const& hash);
|
||||||
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>
|
|
||||||
|
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);
|
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* 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);
|
/** Unshare the node, allowing it to be modified */
|
||||||
SHAMapTreeNode::pointer getNode (const SHAMapNodeID & id, uint256 const& hash, bool modify);
|
void unshareNode (SHAMapTreeNode::pointer&, SHAMapNodeID const& nodeID);
|
||||||
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);
|
|
||||||
|
|
||||||
// Non-blocking version of getNodePointerNT
|
/** prepare a node to be modified before flushing */
|
||||||
SHAMapTreeNode* getNodeAsync (
|
void preFlushNode (SHAMapTreeNode::pointer& node);
|
||||||
const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter, bool& pending);
|
|
||||||
|
|
||||||
SHAMapItem::pointer onlyBelow (SHAMapTreeNode*, SHAMapNodeID);
|
/** write and canonicalize modified node */
|
||||||
void eraseChildren (SHAMapTreeNode::pointer, SHAMapNodeID);
|
void writeNode (NodeObjectType t, std::uint32_t seq,
|
||||||
void dropBelow (SHAMapTreeNode*, SHAMapNodeID);
|
SHAMapTreeNode::pointer& node);
|
||||||
bool hasInnerNode (const SHAMapNodeID & nodeID, uint256 const& hash);
|
|
||||||
|
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 hasLeafNode (uint256 const& tag, uint256 const& hash);
|
||||||
|
|
||||||
bool walkBranch (SHAMapTreeNode* node, SHAMapNodeID nodeID,
|
bool walkBranch (SHAMapTreeNode* node,
|
||||||
SHAMapItem::ref otherMapItem, bool isFirstMap,
|
SHAMapItem::ref otherMapItem, bool isFirstMap,
|
||||||
Delta & differences, int & maxCount);
|
Delta & differences, int & maxCount);
|
||||||
|
|
||||||
void visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function);
|
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.
|
private:
|
||||||
// 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;
|
|
||||||
|
|
||||||
FullBelowCache& m_fullBelowCache;
|
FullBelowCache& m_fullBelowCache;
|
||||||
std::uint32_t mSeq;
|
std::uint32_t mSeq;
|
||||||
std::uint32_t mLedgerSeq; // sequence number of ledger this is part of
|
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;
|
TreeNodeCache& mTreeNodeCache;
|
||||||
SHAMapTreeNode::pointer root;
|
SHAMapTreeNode::pointer root;
|
||||||
SHAMapState mState;
|
SHAMapState mState;
|
||||||
SHAMapType mType;
|
SHAMapType mType;
|
||||||
bool mTXMap; // Map of transactions without metadata
|
bool mBacked; // Map is backed by the database
|
||||||
MissingNodeHandler m_missing_node_handler;
|
MissingNodeHandler m_missing_node_handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,47 +25,29 @@ namespace ripple {
|
|||||||
// branches with the same branch hash. A limit can be passed so
|
// 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
|
// 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
|
// makes no sense at all. (And our sync algorithm will avoid
|
||||||
// synchronizing matching brances too.)
|
// synchronizing matching branches too.)
|
||||||
|
|
||||||
class SHAMapDeltaNode
|
bool SHAMap::walkBranch (SHAMapTreeNode* node,
|
||||||
{
|
|
||||||
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,
|
|
||||||
SHAMapItem::ref otherMapItem, bool isFirstMap,
|
SHAMapItem::ref otherMapItem, bool isFirstMap,
|
||||||
Delta& differences, int& maxCount)
|
Delta& differences, int& maxCount)
|
||||||
{
|
{
|
||||||
// Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map
|
// 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;
|
std::stack <SHAMapTreeNode*, std::vector<SHAMapTreeNode*>> nodeStack;
|
||||||
nodeStack.push ({node, nodeID});
|
nodeStack.push ({node});
|
||||||
|
|
||||||
bool emptyBranch = !otherMapItem;
|
bool emptyBranch = !otherMapItem;
|
||||||
|
|
||||||
while (!nodeStack.empty ())
|
while (!nodeStack.empty ())
|
||||||
{
|
{
|
||||||
std::tie(node, nodeID) = nodeStack.top ();
|
node = nodeStack.top ();
|
||||||
nodeStack.pop ();
|
nodeStack.pop ();
|
||||||
|
|
||||||
if (node->isInner ())
|
if (node->isInner ())
|
||||||
{
|
{
|
||||||
// This is an inner node, add all non-empty branches
|
// This is an inner node, add all non-empty branches
|
||||||
uint256 childNodeHash;
|
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = 0; i < 16; ++i)
|
||||||
{
|
if (!node->isEmptyBranch (i))
|
||||||
SHAMapNodeID childNodeID = nodeID;
|
nodeStack.push ({descendThrow (node, i)});
|
||||||
if (node->descend (i, childNodeID, childNodeHash))
|
|
||||||
nodeStack.push ({getNodePointer (childNodeID, childNodeHash),
|
|
||||||
childNodeID});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -136,27 +118,23 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
|
|||||||
|
|
||||||
assert (isValid () && otherMap && otherMap->isValid ());
|
assert (isValid () && otherMap && otherMap->isValid ());
|
||||||
|
|
||||||
std::stack<SHAMapDeltaNode> nodeStack; // track nodes we've pushed
|
using StackEntry = std::pair <SHAMapTreeNode*, SHAMapTreeNode*>;
|
||||||
|
std::stack <StackEntry, std::vector<StackEntry>> nodeStack; // track nodes we've pushed
|
||||||
ScopedReadLockType sl (mLock);
|
|
||||||
|
|
||||||
if (getHash () == otherMap->getHash ())
|
if (getHash () == otherMap->getHash ())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
nodeStack.push (SHAMapDeltaNode (SHAMapNodeID (), getHash (),
|
nodeStack.push ({root.get(), otherMap->root.get()});
|
||||||
otherMap->getHash ()));
|
|
||||||
while (!nodeStack.empty ())
|
while (!nodeStack.empty ())
|
||||||
{
|
{
|
||||||
SHAMapDeltaNode dNode (nodeStack.top ());
|
SHAMapTreeNode* ourNode = nodeStack.top().first;
|
||||||
|
SHAMapTreeNode* otherNode = nodeStack.top().second;
|
||||||
nodeStack.pop ();
|
nodeStack.pop ();
|
||||||
|
|
||||||
SHAMapTreeNode* ourNode = getNodePointer (dNode.mNodeID, dNode.mOurHash);
|
|
||||||
SHAMapTreeNode* otherNode = otherMap->getNodePointer (dNode.mNodeID,
|
|
||||||
dNode.mOtherHash);
|
|
||||||
if (!ourNode || !otherNode)
|
if (!ourNode || !otherNode)
|
||||||
{
|
{
|
||||||
assert (false);
|
assert (false);
|
||||||
throw SHAMapMissingNode (mType, dNode.mNodeID, uint256 ());
|
throw SHAMapMissingNode (mType, uint256 ());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ourNode->isLeaf () && otherNode->isLeaf ())
|
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 ())
|
else if (ourNode->isInner () && otherNode->isLeaf ())
|
||||||
{
|
{
|
||||||
if (!walkBranch (ourNode, dNode.mNodeID, otherNode->peekItem (),
|
if (!walkBranch (ourNode, otherNode->peekItem (),
|
||||||
true, differences, maxCount))
|
true, differences, maxCount))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (ourNode->isLeaf () && otherNode->isInner ())
|
else if (ourNode->isLeaf () && otherNode->isInner ())
|
||||||
{
|
{
|
||||||
if (!otherMap->walkBranch (otherNode, dNode.mNodeID,
|
if (!otherMap->walkBranch (otherNode, ourNode->peekItem (),
|
||||||
ourNode->peekItem (), false, differences, maxCount))
|
false, differences, maxCount))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (ourNode->isInner () && otherNode->isInner ())
|
else if (ourNode->isInner () && otherNode->isInner ())
|
||||||
@@ -208,10 +186,8 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
|
|||||||
if (otherNode->isEmptyBranch (i))
|
if (otherNode->isEmptyBranch (i))
|
||||||
{
|
{
|
||||||
// We have a branch, the other tree does not
|
// We have a branch, the other tree does not
|
||||||
SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i);
|
SHAMapTreeNode* iNode = descendThrow (ourNode, i);
|
||||||
SHAMapTreeNode* iNode = getNodePointer (childNodeID,
|
if (!walkBranch (iNode,
|
||||||
ourNode->getChildHash (i));
|
|
||||||
if (!walkBranch (iNode, childNodeID,
|
|
||||||
SHAMapItem::pointer (), true,
|
SHAMapItem::pointer (), true,
|
||||||
differences, maxCount))
|
differences, maxCount))
|
||||||
return false;
|
return false;
|
||||||
@@ -219,20 +195,16 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
|
|||||||
else if (ourNode->isEmptyBranch (i))
|
else if (ourNode->isEmptyBranch (i))
|
||||||
{
|
{
|
||||||
// The other tree has a branch, we do not
|
// The other tree has a branch, we do not
|
||||||
SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i);
|
|
||||||
SHAMapTreeNode* iNode =
|
SHAMapTreeNode* iNode =
|
||||||
otherMap->getNodePointer(childNodeID,
|
otherMap->descendThrow(otherNode, i);
|
||||||
otherNode->getChildHash (i));
|
if (!otherMap->walkBranch (iNode,
|
||||||
if (!otherMap->walkBranch (iNode, childNodeID,
|
|
||||||
SHAMapItem::pointer(),
|
SHAMapItem::pointer(),
|
||||||
false, differences, maxCount))
|
false, differences, maxCount))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else // The two trees have different non-empty branches
|
else // The two trees have different non-empty branches
|
||||||
nodeStack.push (SHAMapDeltaNode (
|
nodeStack.push ({descendThrow (ourNode, i),
|
||||||
dNode.mNodeID.getChildNodeID (i),
|
otherMap->descendThrow (otherNode, i)});
|
||||||
ourNode->getChildHash (i),
|
|
||||||
otherNode->getChildHash (i)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -244,42 +216,37 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
|
|||||||
|
|
||||||
void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
|
void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
|
||||||
{
|
{
|
||||||
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> nodeStack;
|
std::stack <SHAMapTreeNode::pointer,
|
||||||
|
std::vector <SHAMapTreeNode::pointer>> nodeStack;
|
||||||
ScopedReadLockType sl (mLock);
|
|
||||||
|
|
||||||
if (!root->isInner ()) // root is only node, and we have it
|
if (!root->isInner ()) // root is only node, and we have it
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nodeStack.push ({root, SHAMapNodeID{}});
|
nodeStack.push (root);
|
||||||
|
|
||||||
while (!nodeStack.empty ())
|
while (!nodeStack.empty ())
|
||||||
{
|
{
|
||||||
SHAMapTreeNode::pointer node;
|
SHAMapTreeNode::pointer node = std::move (nodeStack.top());
|
||||||
SHAMapNodeID nodeID;
|
|
||||||
std::tie(node, nodeID) = nodeStack.top ();
|
|
||||||
nodeStack.pop ();
|
nodeStack.pop ();
|
||||||
uint256 childNodeHash;
|
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = 0; i < 16; ++i)
|
||||||
{
|
{
|
||||||
SHAMapNodeID childNodeID = nodeID;
|
if (!node->isEmptyBranch (i))
|
||||||
if (node->descend (i, childNodeID, childNodeHash))
|
|
||||||
{
|
{
|
||||||
try
|
SHAMapTreeNode::pointer nextNode = descendNoStore (node, i);
|
||||||
{
|
|
||||||
SHAMapTreeNode::pointer d = getNode(childNodeID,
|
|
||||||
childNodeHash, false);
|
|
||||||
if (d->isInner ())
|
|
||||||
nodeStack.push ({d, childNodeID});
|
|
||||||
}
|
|
||||||
catch (SHAMapMissingNode& n)
|
|
||||||
{
|
|
||||||
missingNodes.push_back (n);
|
|
||||||
|
|
||||||
if (--maxMissing <= 0)
|
if (nextNode)
|
||||||
return;
|
{
|
||||||
}
|
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 ())
|
switch (mn.getMapType ())
|
||||||
{
|
{
|
||||||
case smtTRANSACTION:
|
case smtTRANSACTION:
|
||||||
out << "Missing/TXN(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")";
|
out << "Missing/TXN(" << mn.getNodeHash () << ")";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case smtSTATE:
|
case smtSTATE:
|
||||||
out << "Missing/STA(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")";
|
out << "Missing/STA(" << mn.getNodeHash () << ")";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case smtFREE:
|
case smtFREE:
|
||||||
default:
|
default:
|
||||||
out << "Missing/" << mn.getNodeID ();
|
out << "Missing/" << mn.getNodeHash ();
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,9 @@ class SHAMapMissingNode : public std::runtime_error
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SHAMapMissingNode (SHAMapType t,
|
SHAMapMissingNode (SHAMapType t,
|
||||||
SHAMapNodeID const& nodeID,
|
|
||||||
uint256 const& nodeHash)
|
uint256 const& nodeHash)
|
||||||
: std::runtime_error ("SHAMapMissingNode")
|
: std::runtime_error ("SHAMapMissingNode")
|
||||||
, mType (t)
|
, mType (t)
|
||||||
, mNodeID (nodeID)
|
|
||||||
, mNodeHash (nodeHash)
|
, mNodeHash (nodeHash)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -51,11 +49,6 @@ public:
|
|||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
SHAMapNodeID const& getNodeID () const
|
|
||||||
{
|
|
||||||
return mNodeID;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 const& getNodeHash () const
|
uint256 const& getNodeHash () const
|
||||||
{
|
{
|
||||||
return mNodeHash;
|
return mNodeHash;
|
||||||
@@ -63,7 +56,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
SHAMapType mType;
|
SHAMapType mType;
|
||||||
SHAMapNodeID mNodeID;
|
|
||||||
uint256 mNodeHash;
|
uint256 mNodeHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ std::string SHAMapNodeID::getRawString () const
|
|||||||
SHAMapNodeID SHAMapNodeID::getChildNodeID (int m) const
|
SHAMapNodeID SHAMapNodeID::getChildNodeID (int m) const
|
||||||
{
|
{
|
||||||
assert ((m >= 0) && (m < 16));
|
assert ((m >= 0) && (m < 16));
|
||||||
|
assert (mDepth <= 64);
|
||||||
|
|
||||||
uint256 child (mNodeID);
|
uint256 child (mNodeID);
|
||||||
child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4);
|
child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4);
|
||||||
|
|||||||
@@ -26,46 +26,52 @@ namespace ripple {
|
|||||||
|
|
||||||
static const uint256 uZero;
|
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
|
// Adapt visitNodes to visitLeaves
|
||||||
// a lock on the map we're visiting
|
if (!node.isInner ())
|
||||||
snapShot (false)->visitLeavesInternal (function);
|
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 ());
|
assert (root->isValid ());
|
||||||
|
|
||||||
if (!root || root->isEmpty ())
|
if (!root || root->isEmpty ())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!root->isInner ())
|
function (*root);
|
||||||
{
|
|
||||||
function (root->peekItem ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stack<std::tuple<int, SHAMapTreeNode*, SHAMapNodeID>> stack;
|
if (!root->isInner ())
|
||||||
SHAMapTreeNode* node = root.get ();
|
return;
|
||||||
SHAMapNodeID nodeID;
|
|
||||||
|
using StackEntry = std::pair <int, SHAMapTreeNode::pointer>;
|
||||||
|
std::stack <StackEntry, std::vector <StackEntry>> stack;
|
||||||
|
|
||||||
|
SHAMapTreeNode::pointer node = root;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
while (pos < 16)
|
while (pos < 16)
|
||||||
{
|
{
|
||||||
SHAMapNodeID childID = nodeID;
|
|
||||||
uint256 childHash;
|
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 ())
|
if (child->isLeaf ())
|
||||||
{
|
|
||||||
function (child->peekItem ());
|
|
||||||
mTNByID.erase (childID); // don't need this leaf anymore
|
|
||||||
++pos;
|
++pos;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If there are no more children, don't push this node
|
// 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)
|
if (pos != 15)
|
||||||
{
|
{
|
||||||
// save next position to resume at
|
// 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
|
// descend to the child's first position
|
||||||
node = child;
|
node = child;
|
||||||
nodeID = childID;
|
|
||||||
pos = 0;
|
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 ())
|
if (stack.empty ())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
std::tie(pos, node, nodeID) = stack.top ();
|
std::tie(pos, node) = stack.top ();
|
||||||
stack.pop ();
|
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,
|
void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
|
||||||
SHAMapSyncFilter* filter)
|
SHAMapSyncFilter* filter)
|
||||||
{
|
{
|
||||||
ScopedReadLockType sl (mLock);
|
|
||||||
|
|
||||||
assert (root->isValid ());
|
assert (root->isValid ());
|
||||||
assert (root->getNodeHash().isNonZero ());
|
assert (root->getNodeHash().isNonZero ());
|
||||||
|
|
||||||
|
|
||||||
if (root->isFullBelow ())
|
if (root->isFullBelow ())
|
||||||
{
|
{
|
||||||
clearSynching ();
|
clearSynching ();
|
||||||
@@ -136,11 +133,12 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
std::vector <std::pair <SHAMapNodeID, uint256>> deferredReads;
|
std::vector <std::tuple <SHAMapTreeNode*, int, SHAMapNodeID>> deferredReads;
|
||||||
deferredReads.reserve (maxDefer + 16);
|
deferredReads.reserve (maxDefer + 16);
|
||||||
|
|
||||||
std::stack <std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>>
|
using StackEntry = std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>;
|
||||||
stack;
|
std::stack <StackEntry, std::vector<StackEntry>> stack;
|
||||||
|
|
||||||
// Traverse the map without blocking
|
// Traverse the map without blocking
|
||||||
|
|
||||||
SHAMapTreeNode *node = root.get ();
|
SHAMapTreeNode *node = root.get ();
|
||||||
@@ -164,11 +162,11 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
|
|||||||
{
|
{
|
||||||
uint256 const& childHash = node->getChildHash (branch);
|
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);
|
SHAMapNodeID childID = nodeID.getChildNodeID (branch);
|
||||||
bool pending = false;
|
bool pending = false;
|
||||||
SHAMapTreeNode* d = getNodeAsync (childID, childHash, filter, pending);
|
SHAMapTreeNode* d = descendAsync (node, branch, childID, filter, pending);
|
||||||
|
|
||||||
if (!d)
|
if (!d)
|
||||||
{
|
{
|
||||||
@@ -186,7 +184,7 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// read is deferred
|
// read is deferred
|
||||||
deferredReads.emplace_back (childID, childHash);
|
deferredReads.emplace_back (node, branch, childID);
|
||||||
}
|
}
|
||||||
|
|
||||||
fullBelow = false; // This node is not known full below
|
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)
|
if (fullBelow)
|
||||||
{ // No partial node encountered below this node
|
{ // No partial node encountered below this node
|
||||||
node->setFullBelow ();
|
node->setFullBelow ();
|
||||||
if (mType == smtSTATE)
|
if (mBacked)
|
||||||
m_fullBelowCache.insert (node->getNodeHash ());
|
m_fullBelowCache.insert (node->getNodeHash ());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,10 +236,19 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
|
|||||||
// Process all deferred reads
|
// Process all deferred reads
|
||||||
for (auto const& node : deferredReads)
|
for (auto const& node : deferredReads)
|
||||||
{
|
{
|
||||||
auto const& nodeID = node.first;
|
auto parent = std::get<0>(node);
|
||||||
auto const& nodeHash = node.second;
|
auto branch = std::get<1>(node);
|
||||||
SHAMapTreeNode* nodePtr = getNodePointerNT (nodeID, nodeHash, filter);
|
auto const& nodeID = std::get<2>(node);
|
||||||
if (!nodePtr && missingHashes.insert (nodeHash).second)
|
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);
|
nodeIDs.push_back (nodeID);
|
||||||
hashes.push_back (nodeHash);
|
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)
|
std::list<Blob >& rawNodes, bool fatRoot, bool fatLeaves)
|
||||||
{
|
{
|
||||||
// Gets a node and some of its children
|
// 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;
|
WriteLog (lsWARNING, SHAMap) << "peer requested node that is not in the map: " << wanted;
|
||||||
throw std::runtime_error ("Peer requested node not in map");
|
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;
|
Serializer s;
|
||||||
node->addRaw (s, snfWIRE);
|
node->addRaw (s, snfWIRE);
|
||||||
nodeIDs.push_back(wanted);
|
nodeIDs.push_back (wanted);
|
||||||
rawNodes.push_back (s.peekData ());
|
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
|
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;
|
count = 0;
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = 0; i < 16; ++i)
|
||||||
{
|
{
|
||||||
SHAMapNodeID tempNodeID = wanted;
|
if (!node->isEmptyBranch (i))
|
||||||
uint256 nextNodeHash;
|
|
||||||
if (node->descend (i, tempNodeID, nextNodeHash))
|
|
||||||
{
|
{
|
||||||
nextNodeID = tempNodeID;
|
SHAMapNodeID nextNodeID = wanted.getChildNodeID (i);
|
||||||
nextNode = getNodePointer (nextNodeID, nextNodeHash);
|
nextNode = descendThrow (node, i);
|
||||||
++count;
|
++count;
|
||||||
if (fatLeaves || nextNode->isInner ())
|
if (fatLeaves || nextNode->isInner ())
|
||||||
{
|
{
|
||||||
Serializer s;
|
Serializer s;
|
||||||
nextNode->addRaw (s, snfWIRE);
|
nextNode->addRaw (s, snfWIRE);
|
||||||
nodeIDs.push_back (nextNodeID);
|
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
|
skipNode = true; // Don't add this node again if we loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node = nextNode;
|
node = nextNode;
|
||||||
wanted = nextNodeID;
|
wanted = nextNodeID;
|
||||||
|
|
||||||
@@ -341,7 +355,6 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
|
|||||||
|
|
||||||
bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
|
bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
|
||||||
{
|
{
|
||||||
ScopedReadLockType sl (mLock);
|
|
||||||
root->addRaw (s, format);
|
root->addRaw (s, format);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -349,8 +362,6 @@ bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
|
|||||||
SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
|
SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
|
||||||
SHAMapSyncFilter* filter)
|
SHAMapSyncFilter* filter)
|
||||||
{
|
{
|
||||||
ScopedWriteLockType sl (mLock);
|
|
||||||
|
|
||||||
// we already have a root node
|
// we already have a root node
|
||||||
if (root->getNodeHash ().isNonZero ())
|
if (root->getNodeHash ().isNonZero ())
|
||||||
{
|
{
|
||||||
@@ -360,7 +371,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
|
|||||||
|
|
||||||
assert (mSeq >= 1);
|
assert (mSeq >= 1);
|
||||||
SHAMapTreeNode::pointer node =
|
SHAMapTreeNode::pointer node =
|
||||||
std::make_shared<SHAMapTreeNode> (rootNode, mSeq - 1,
|
std::make_shared<SHAMapTreeNode> (rootNode, 0,
|
||||||
format, uZero, false);
|
format, uZero, false);
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
@@ -370,8 +381,10 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
|
|||||||
node->dump (SHAMapNodeID ());
|
node->dump (SHAMapNodeID ());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (mBacked)
|
||||||
|
canonicalize (node->getNodeHash (), node);
|
||||||
|
|
||||||
root = node;
|
root = node;
|
||||||
mTNByID.replace(SHAMapNodeID{}, root);
|
|
||||||
|
|
||||||
if (root->isLeaf())
|
if (root->isLeaf())
|
||||||
clearSynching ();
|
clearSynching ();
|
||||||
@@ -390,8 +403,6 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
|
|||||||
SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format,
|
SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format,
|
||||||
SHAMapSyncFilter* filter)
|
SHAMapSyncFilter* filter)
|
||||||
{
|
{
|
||||||
ScopedWriteLockType sl (mLock);
|
|
||||||
|
|
||||||
// we already have a root node
|
// we already have a root node
|
||||||
if (root->getNodeHash ().isNonZero ())
|
if (root->getNodeHash ().isNonZero ())
|
||||||
{
|
{
|
||||||
@@ -402,14 +413,16 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH
|
|||||||
|
|
||||||
assert (mSeq >= 1);
|
assert (mSeq >= 1);
|
||||||
SHAMapTreeNode::pointer node =
|
SHAMapTreeNode::pointer node =
|
||||||
std::make_shared<SHAMapTreeNode> (rootNode, mSeq - 1,
|
std::make_shared<SHAMapTreeNode> (rootNode, 0,
|
||||||
format, uZero, false);
|
format, uZero, false);
|
||||||
|
|
||||||
if (!node || node->getNodeHash () != hash)
|
if (!node || node->getNodeHash () != hash)
|
||||||
return SHAMapAddNode::invalid ();
|
return SHAMapAddNode::invalid ();
|
||||||
|
|
||||||
|
if (mBacked)
|
||||||
|
canonicalize (hash, node);
|
||||||
|
|
||||||
root = node;
|
root = node;
|
||||||
mTNByID.replace(SHAMapNodeID{}, root);
|
|
||||||
|
|
||||||
if (root->isLeaf())
|
if (root->isLeaf())
|
||||||
clearSynching ();
|
clearSynching ();
|
||||||
@@ -429,8 +442,6 @@ SHAMapAddNode
|
|||||||
SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
||||||
SHAMapSyncFilter* filter)
|
SHAMapSyncFilter* filter)
|
||||||
{
|
{
|
||||||
ScopedWriteLockType sl (mLock);
|
|
||||||
|
|
||||||
// return value: true=okay, false=error
|
// return value: true=okay, false=error
|
||||||
assert (!node.isRoot ());
|
assert (!node.isRoot ());
|
||||||
|
|
||||||
@@ -440,18 +451,10 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
|||||||
return SHAMapAddNode::duplicate ();
|
return SHAMapAddNode::duplicate ();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkCacheNode (node)) // Do we already have this node?
|
SHAMapNodeID iNodeID;
|
||||||
return SHAMapAddNode::duplicate ();
|
SHAMapTreeNode* iNode = root.get ();
|
||||||
|
|
||||||
SHAMapNodeID iNodeID = node.getParentNodeID();
|
while (iNode->isInner () && !iNode->isFullBelow () &&
|
||||||
SHAMapTreeNode* iNode = checkCacheNode(iNodeID).get();
|
|
||||||
if (iNode == nullptr)
|
|
||||||
{
|
|
||||||
iNode = root.get ();
|
|
||||||
iNodeID = SHAMapNodeID{};
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!iNode->isLeaf () && !iNode->isFullBelow () &&
|
|
||||||
(iNodeID.getDepth () < node.getDepth ()))
|
(iNodeID.getDepth () < node.getDepth ()))
|
||||||
{
|
{
|
||||||
int branch = iNodeID.selectBranch (node.getNodeID ());
|
int branch = iNodeID.selectBranch (node.getNodeID ());
|
||||||
@@ -467,12 +470,13 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
|||||||
uint256 childHash = iNode->getChildHash (branch);
|
uint256 childHash = iNode->getChildHash (branch);
|
||||||
if (m_fullBelowCache.touch_if_exists (childHash))
|
if (m_fullBelowCache.touch_if_exists (childHash))
|
||||||
return SHAMapAddNode::duplicate ();
|
return SHAMapAddNode::duplicate ();
|
||||||
SHAMapNodeID nextNodeID = iNodeID.getChildNodeID (branch);
|
|
||||||
SHAMapTreeNode* nextNode = getNodePointerNT(nextNodeID, childHash,
|
SHAMapTreeNode* prevNode = iNode;
|
||||||
filter);
|
std::tie (iNode, iNodeID) = descend (iNode, iNodeID, branch, filter);
|
||||||
if (!nextNode)
|
|
||||||
|
if (!iNode)
|
||||||
{
|
{
|
||||||
if (iNodeID.getDepth () != (node.getDepth () - 1))
|
if (iNodeID != node)
|
||||||
{
|
{
|
||||||
// Either this node is broken or we didn't request it (yet)
|
// Either this node is broken or we didn't request it (yet)
|
||||||
WriteLog (lsWARNING, SHAMap) << "unable to hook node " << node;
|
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,
|
std::make_shared<SHAMapTreeNode> (rawNode, 0, snfWIRE,
|
||||||
uZero, false);
|
uZero, false);
|
||||||
|
|
||||||
if (childHash != newNode->getNodeHash ())
|
if (!newNode->isInBounds (iNodeID))
|
||||||
{
|
|
||||||
WriteLog (lsWARNING, SHAMap) << "Corrupt node received";
|
|
||||||
return SHAMapAddNode::invalid ();
|
|
||||||
}
|
|
||||||
|
|
||||||
canonicalize (childHash, newNode);
|
|
||||||
|
|
||||||
if (!iNode->isInBounds (iNodeID))
|
|
||||||
{
|
{
|
||||||
// Map is provably invalid
|
// Map is provably invalid
|
||||||
mState = smsInvalid;
|
mState = smsInvalid;
|
||||||
return SHAMapAddNode::useful ();
|
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;
|
Serializer s;
|
||||||
newNode->addRaw (s, snfPREFIX);
|
newNode->addRaw (s, snfPREFIX);
|
||||||
@@ -512,8 +519,6 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
|||||||
|
|
||||||
return SHAMapAddNode::useful ();
|
return SHAMapAddNode::useful ();
|
||||||
}
|
}
|
||||||
iNode = nextNode;
|
|
||||||
iNodeID = nextNodeID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteLog (lsTRACE, SHAMap) << "got node, already had it (late)";
|
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)
|
bool SHAMap::deepCompare (SHAMap& other)
|
||||||
{
|
{
|
||||||
// Intended for debug/test only
|
// Intended for debug/test only
|
||||||
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> stack;
|
std::stack <std::pair <SHAMapTreeNode*, SHAMapTreeNode*> > stack;
|
||||||
ScopedReadLockType sl (mLock);
|
|
||||||
|
|
||||||
stack.push ({root, SHAMapNodeID{}});
|
stack.push ({root.get(), other.root.get()});
|
||||||
|
|
||||||
while (!stack.empty ())
|
while (!stack.empty ())
|
||||||
{
|
{
|
||||||
SHAMapTreeNode::pointer node;
|
SHAMapTreeNode *node, *otherNode;
|
||||||
SHAMapNodeID nodeID;
|
std::tie(node, otherNode) = stack.top ();
|
||||||
std::tie(node, nodeID) = stack.top ();
|
|
||||||
stack.pop ();
|
stack.pop ();
|
||||||
|
|
||||||
SHAMapTreeNode::pointer otherNode;
|
if (!node || !otherNode)
|
||||||
|
|
||||||
if (nodeID.isRoot ())
|
|
||||||
otherNode = other.root;
|
|
||||||
else
|
|
||||||
otherNode = other.getNode (nodeID, node->getNodeHash (), false);
|
|
||||||
|
|
||||||
if (!otherNode)
|
|
||||||
{
|
{
|
||||||
WriteLog (lsINFO, SHAMap) << "unable to fetch node";
|
WriteLog (lsINFO, SHAMap) << "unable to fetch node";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (otherNode->getNodeHash () != node->getNodeHash ())
|
else if (otherNode->getNodeHash () != node->getNodeHash ())
|
||||||
{
|
{
|
||||||
WriteLog (lsWARNING, SHAMap) << "node hash mismatch " << nodeID;
|
WriteLog (lsWARNING, SHAMap) << "node hash mismatch";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteLog (lsTRACE) << "Comparing inner nodes " << *node;
|
// WriteLog (lsTRACE) << "Comparing inner nodes " << *node;
|
||||||
|
|
||||||
if (node->getNodeHash () != otherNode->getNodeHash ())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (node->isLeaf ())
|
if (node->isLeaf ())
|
||||||
{
|
{
|
||||||
if (!otherNode->isLeaf ())
|
if (!otherNode->isLeaf ())
|
||||||
@@ -583,16 +576,17 @@ bool SHAMap::deepCompare (SHAMap& other)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SHAMapNodeID nextNodeID = nodeID;
|
if (otherNode->isEmptyBranch (i))
|
||||||
uint256 nextNodeHash;
|
return false;
|
||||||
if (!node->descend (i, nextNodeID, nextNodeHash))
|
|
||||||
|
SHAMapTreeNode *next = descend (node, i);
|
||||||
|
SHAMapTreeNode *otherNext = other.descend (otherNode, i);
|
||||||
|
if (!next || !otherNext)
|
||||||
{
|
{
|
||||||
WriteLog (lsWARNING, SHAMap) << "unable to fetch inner node";
|
WriteLog (lsWARNING, SHAMap) << "unable to fetch inner node";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SHAMapTreeNode::pointer next = getNode (nextNodeID,
|
stack.push ({next, otherNext});
|
||||||
nextNodeHash, false);
|
|
||||||
stack.push ({next, nextNodeID});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -602,49 +596,50 @@ bool SHAMap::deepCompare (SHAMap& other)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Does this map have this inner node?
|
/** Does this map have this inner node?
|
||||||
You must hold a read lock to call this function
|
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
|
SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
|
||||||
uint256 const& targetNodeHash)
|
uint256 const& targetNodeHash)
|
||||||
{
|
{
|
||||||
SHAMapTreeNode::pointer ptr = mTNByID.retrieve (targetNodeID);
|
|
||||||
if (ptr)
|
|
||||||
return ptr->getNodeHash() == targetNodeHash;
|
|
||||||
|
|
||||||
SHAMapTreeNode* node = root.get ();
|
SHAMapTreeNode* node = root.get ();
|
||||||
SHAMapNodeID nodeID;
|
SHAMapNodeID nodeID;
|
||||||
uint256 nodeHash;
|
|
||||||
while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ()))
|
while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ()))
|
||||||
{
|
{
|
||||||
int branch = nodeID.selectBranch (targetNodeID.getNodeID ());
|
int branch = nodeID.selectBranch (targetNodeID.getNodeID ());
|
||||||
if (!node->descend (branch, nodeID, nodeHash))
|
|
||||||
|
if (node->isEmptyBranch (branch))
|
||||||
return false;
|
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?
|
/** 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)
|
bool SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash)
|
||||||
{
|
{
|
||||||
SHAMapTreeNode* node = root.get ();
|
SHAMapTreeNode* node = root.get ();
|
||||||
SHAMapNodeID nodeID;
|
SHAMapNodeID nodeID;
|
||||||
uint256 nodeHash;
|
|
||||||
if (!node->isInner()) // only one leaf node in the tree
|
if (!node->isInner()) // only one leaf node in the tree
|
||||||
return node->getNodeHash() == targetNodeHash;
|
return node->getNodeHash() == targetNodeHash;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
int branch = nodeID.selectBranch (tag);
|
int branch = nodeID.selectBranch (tag);
|
||||||
if (!node->descend (branch, nodeID, nodeHash))
|
|
||||||
|
if (node->isEmptyBranch (branch))
|
||||||
return false; // Dead end, node must not be here
|
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;
|
return true;
|
||||||
node = getNodePointer (nodeID, nodeHash);
|
|
||||||
|
node = descendThrow (node, branch);
|
||||||
|
nodeID = nodeID.getChildNodeID (branch);
|
||||||
}
|
}
|
||||||
while (node->isInner());
|
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,
|
void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
|
||||||
std::function<void (uint256 const&, const Blob&)> func)
|
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 ())
|
if (root->getNodeHash ().isZero ())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -695,14 +678,16 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// contains unexplored non-matching inner node entries
|
// 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{}});
|
stack.push ({root.get(), SHAMapNodeID{}});
|
||||||
|
|
||||||
while (!stack.empty() && (max > 0))
|
while (!stack.empty() && (max > 0))
|
||||||
{
|
{
|
||||||
SHAMapTreeNode* node;
|
SHAMapTreeNode* node;
|
||||||
SHAMapNodeID nodeID;
|
SHAMapNodeID nodeID;
|
||||||
std::tie(node, nodeID) = stack.top ();
|
std::tie (node, nodeID) = stack.top ();
|
||||||
stack.pop ();
|
stack.pop ();
|
||||||
|
|
||||||
// 1) Add this node to the pack
|
// 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);
|
uint256 const& childHash = node->getChildHash (i);
|
||||||
SHAMapNodeID childID = nodeID.getChildNodeID (i);
|
SHAMapNodeID childID = nodeID.getChildNodeID (i);
|
||||||
|
SHAMapTreeNode* next = descendThrow (node, i);
|
||||||
SHAMapTreeNode* next = getNodePointer (childID, childHash);
|
|
||||||
|
|
||||||
if (next->isInner ())
|
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);
|
auto stack = getStack (index, false);
|
||||||
|
|
||||||
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> stack =
|
|
||||||
getStack (index, false);
|
|
||||||
if (stack.empty () || !stack.top ().first->isLeaf ())
|
if (stack.empty () || !stack.top ().first->isLeaf ())
|
||||||
throw std::runtime_error ("requested leaf not present");
|
throw std::runtime_error ("requested leaf not present");
|
||||||
|
|
||||||
@@ -796,6 +777,7 @@ public:
|
|||||||
{
|
{
|
||||||
log <<
|
log <<
|
||||||
"Unable to add item to map";
|
"Unable to add item to map";
|
||||||
|
assert (false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -806,6 +788,7 @@ public:
|
|||||||
{
|
{
|
||||||
log <<
|
log <<
|
||||||
"Unable to remove item from map";
|
"Unable to remove item from map";
|
||||||
|
assert (false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -813,7 +796,8 @@ public:
|
|||||||
if (beforeHash != map.getHash ())
|
if (beforeHash != map.getHash ())
|
||||||
{
|
{
|
||||||
log <<
|
log <<
|
||||||
"Hashes do not match";
|
"Hashes do not match " << beforeHash << " " << map.getHash ();
|
||||||
|
assert (false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,10 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
std::mutex SHAMapTreeNode::childLock;
|
||||||
|
|
||||||
SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq)
|
SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq)
|
||||||
: mHash (std::uint64_t(0))
|
: mSeq (seq)
|
||||||
, mSeq (seq)
|
|
||||||
, mAccessSeq (seq)
|
|
||||||
, mType (tnERROR)
|
, mType (tnERROR)
|
||||||
, mIsBranch (0)
|
, mIsBranch (0)
|
||||||
, mFullBelow (false)
|
, mFullBelow (false)
|
||||||
@@ -41,7 +41,14 @@ SHAMapTreeNode::SHAMapTreeNode (const SHAMapTreeNode& node, std::uint32_t seq)
|
|||||||
if (node.mItem)
|
if (node.mItem)
|
||||||
mItem = node.mItem;
|
mItem = node.mItem;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
memcpy (mHashes, node.mHashes, sizeof (mHashes));
|
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,
|
SHAMapTreeNode::SHAMapTreeNode (SHAMapItem::ref item,
|
||||||
@@ -471,11 +478,13 @@ std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const
|
|||||||
return ret;
|
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 ((m >= 0) && (m < 16));
|
||||||
assert (mType == tnINNER);
|
assert (mType == tnINNER);
|
||||||
assert (mSeq != 0);
|
assert (mSeq != 0);
|
||||||
|
assert (child.get() != this);
|
||||||
|
|
||||||
if (mHashes[m] == hash)
|
if (mHashes[m] == hash)
|
||||||
return false;
|
return false;
|
||||||
@@ -483,31 +492,72 @@ bool SHAMapTreeNode::setChildHash (int m, uint256 const& hash)
|
|||||||
mHashes[m] = hash;
|
mHashes[m] = hash;
|
||||||
|
|
||||||
if (hash.isNonZero ())
|
if (hash.isNonZero ())
|
||||||
|
{
|
||||||
|
assert (child && (child->getNodeHash() == hash));
|
||||||
mIsBranch |= (1 << m);
|
mIsBranch |= (1 << m);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
assert (!child);
|
||||||
mIsBranch &= ~ (1 << m);
|
mIsBranch &= ~ (1 << m);
|
||||||
|
}
|
||||||
|
|
||||||
|
mChildren[m] = child;
|
||||||
|
|
||||||
return updateHash ();
|
return updateHash ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Descends along the specified branch
|
// finished modifying, now make shareable
|
||||||
// On invocation, nodeID must be the ID of this node
|
void SHAMapTreeNode::shareChild (int m, SHAMapTreeNode::ref child)
|
||||||
// Returns false if there is no node down that branch
|
{
|
||||||
// Otherwise, returns true and fills in the node's ID and hash
|
assert ((m >= 0) && (m < 16));
|
||||||
|
assert (mType == tnINNER);
|
||||||
|
assert (mSeq != 0);
|
||||||
|
assert (child);
|
||||||
|
assert (child.get() != this);
|
||||||
|
assert (child->getNodeHash() == mHashes[m]);
|
||||||
|
|
||||||
bool
|
mChildren[m] = child;
|
||||||
SHAMapTreeNode::descend (int branch, SHAMapNodeID& nodeID, uint256& nodeHash)
|
}
|
||||||
|
|
||||||
|
SHAMapTreeNode* SHAMapTreeNode::getChildPointer (int branch)
|
||||||
{
|
{
|
||||||
assert (branch >= 0 && branch < 16);
|
assert (branch >= 0 && branch < 16);
|
||||||
assert (isInnerNode ());
|
assert (isInnerNode ());
|
||||||
|
|
||||||
if (isEmptyBranch (branch))
|
std::unique_lock <std::mutex> lock (childLock);
|
||||||
return false;
|
return mChildren[branch].get ();
|
||||||
|
|
||||||
nodeID = nodeID.getChildNodeID (branch);
|
|
||||||
nodeHash = mHashes [branch];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
} // ripple
|
||||||
|
|||||||
@@ -77,12 +77,7 @@ public:
|
|||||||
}
|
}
|
||||||
void setSeq (std::uint32_t s)
|
void setSeq (std::uint32_t s)
|
||||||
{
|
{
|
||||||
mAccessSeq = mSeq = s;
|
mSeq = s;
|
||||||
}
|
|
||||||
void touch (std::uint32_t s)
|
|
||||||
{
|
|
||||||
if (mSeq != 0)
|
|
||||||
mAccessSeq = s;
|
|
||||||
}
|
}
|
||||||
uint256 const& getNodeHash () const
|
uint256 const& getNodeHash () const
|
||||||
{
|
{
|
||||||
@@ -130,7 +125,13 @@ public:
|
|||||||
{
|
{
|
||||||
return !mItem;
|
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
|
bool isEmptyBranch (int m) const
|
||||||
{
|
{
|
||||||
return (mIsBranch & (1 << m)) == 0;
|
return (mIsBranch & (1 << m)) == 0;
|
||||||
@@ -178,32 +179,27 @@ public:
|
|||||||
virtual void dump (SHAMapNodeID const&);
|
virtual void dump (SHAMapNodeID const&);
|
||||||
virtual std::string getString (SHAMapNodeID const&) const;
|
virtual std::string getString (SHAMapNodeID const&) const;
|
||||||
|
|
||||||
/** Descend along the specified branch
|
SHAMapTreeNode* getChildPointer (int branch);
|
||||||
On invocation, nodeID must be the ID of this node
|
SHAMapTreeNode::pointer getChild (int branch);
|
||||||
Returns `false` if there is no node down that branch
|
void canonicalizeChild (int branch, SHAMapTreeNode::pointer& node);
|
||||||
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);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// VFALCO TODO remove the use of friend
|
// VFALCO TODO remove the use of friend
|
||||||
friend class SHAMap;
|
friend class SHAMap;
|
||||||
|
|
||||||
uint256 mHash;
|
uint256 mHash;
|
||||||
uint256 mHashes[16];
|
uint256 mHashes[16];
|
||||||
SHAMapItem::pointer mItem;
|
SHAMapTreeNode::pointer mChildren[16];
|
||||||
std::uint32_t mSeq, mAccessSeq;
|
SHAMapItem::pointer mItem;
|
||||||
TNType mType;
|
std::uint32_t mSeq;
|
||||||
int mIsBranch;
|
TNType mType;
|
||||||
bool mFullBelow;
|
int mIsBranch;
|
||||||
|
bool mFullBelow;
|
||||||
|
|
||||||
bool updateHash ();
|
bool updateHash ();
|
||||||
|
|
||||||
|
static std::mutex childLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
using TreeNodeCache = TaggedCache <uint256, SHAMapTreeNode>;
|
using TreeNodeCache = TaggedCache <uint256, SHAMapTreeNode>;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ TransactionAcquire::TransactionAcquire (uint256 const& hash, clock_type& clock)
|
|||||||
Application& app = getApp();
|
Application& app = getApp();
|
||||||
mMap = std::make_shared<SHAMap> (smtTRANSACTION, hash,
|
mMap = std::make_shared<SHAMap> (smtTRANSACTION, hash,
|
||||||
app.getFullBelowCache (), app.getTreeNodeCache());
|
app.getFullBelowCache (), app.getTreeNodeCache());
|
||||||
mMap->setTXMap ();
|
mMap->setUnbacked ();
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionAcquire::~TransactionAcquire ()
|
TransactionAcquire::~TransactionAcquire ()
|
||||||
|
|||||||
Reference in New Issue
Block a user