Files
xahaud/src/cpp/ripple/SHAMap.h
Vinnie Falco 2c525b03c6 Enormous cleanup of RippleAddress et. al. into ripple_data
Start cleanup into ripple_data, split out some hash_value() instances
Tidy up CBigNum into ripple_data, moving definitions to .cpp
Split and clean up base58 stuff
Remove unused files from VS2012 project
Clean up some bignum stuff and remove unused files
Partial cleanup of RFC1751
Enormous cleanup with RippleAddress and related, into ripple_data
Remove unused VS project files
Move ECIES stuff into CKey
2013-05-30 10:16:22 -07:00

490 lines
17 KiB
C++

#ifndef __SHAMAP__
#define __SHAMAP__
#include <list>
#include <map>
#include <stack>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/unordered_map.hpp>
#include "ScopedLock.h"
#include "HashedObject.h"
#include "InstanceCounter.h"
DEFINE_INSTANCE(SHAMap);
DEFINE_INSTANCE(SHAMapItem);
DEFINE_INSTANCE(SHAMapTreeNode);
class SHAMap;
class SHAMapSyncFilter;
// A tree-like map of SHA256 hashes
// The trees are designed for rapid synchronization and compression of differences
class SHAMapNode
{ // Identifies a node in a SHA256 hash map
private:
static uint256 smMasks[65]; // AND with hash to get node id
uint256 mNodeID;
int mDepth;
mutable size_t mHash;
void setMHash() const;
protected:
SHAMapNode(int depth, const uint256& id, bool) : mNodeID(id), mDepth(depth), mHash(0) { ; }
public:
static const int rootDepth = 0;
SHAMapNode() : mDepth(0), mHash(0) { ; }
SHAMapNode(int depth, const uint256& hash);
int getDepth() const { return mDepth; }
const uint256& getNodeID() const { return mNodeID; }
bool isValid() const { return (mDepth >= 0) && (mDepth < 64); }
bool isRoot() const { return mDepth == 0; }
size_t getMHash() const { if (mHash == 0) setMHash(); return mHash; }
virtual bool isPopulated() const { return false; }
SHAMapNode getParentNodeID() const
{
assert(mDepth);
return SHAMapNode(mDepth - 1, mNodeID);
}
SHAMapNode getChildNodeID(int m) const;
int selectBranch(const uint256& hash) const;
bool operator<(const SHAMapNode&) const;
bool operator>(const SHAMapNode&) const;
bool operator<=(const SHAMapNode&) const;
bool operator>=(const SHAMapNode&) const;
bool operator==(const SHAMapNode& n) const { return (mDepth == n.mDepth) && (mNodeID == n.mNodeID); }
bool operator==(const uint256& n) const { return n == mNodeID; }
bool operator!=(const SHAMapNode& n) const { return (mDepth != n.mDepth) || (mNodeID != n.mNodeID); }
bool operator!=(const uint256& n) const { return n != mNodeID; }
virtual std::string getString() const;
void dump() const;
static bool ClassInit();
static uint256 getNodeID(int depth, const uint256& hash);
// Convert to/from wire format (256-bit nodeID, 1-byte depth)
void addIDRaw(Serializer &s) const;
std::string getRawString() const;
static int getRawIDLength(void) { return 33; }
SHAMapNode(const void *ptr, int len);
};
extern std::size_t hash_value(const SHAMapNode& mn);
inline std::ostream& operator<<(std::ostream& out, const SHAMapNode& node) { return out << node.getString(); }
class SHAMapItem : public IS_INSTANCE(SHAMapItem)
{ // an item stored in a SHAMap
public:
typedef boost::shared_ptr<SHAMapItem> pointer;
typedef const boost::shared_ptr<SHAMapItem>& ref;
private:
uint256 mTag;
Serializer mData;
public:
SHAMapItem(const uint256& tag) : mTag(tag) { ; }
SHAMapItem(const uint256& tag, const std::vector<unsigned char>& data);
SHAMapItem(const uint256& tag, const Serializer& s);
SHAMapItem(const std::vector<unsigned char>& data); // tag by hash
const uint256& getTag() const { return mTag; }
std::vector<unsigned char> getData() const { return mData.getData(); }
const std::vector<unsigned char>& peekData() const { return mData.peekData(); }
Serializer& peekSerializer() { return mData; }
void addRaw(std::vector<unsigned char>& s) const { s.insert(s.end(), mData.begin(), mData.end()); }
void updateData(const std::vector<unsigned char>& data) { mData=data; }
bool operator==(const SHAMapItem& i) const { return mTag == i.mTag; }
bool operator!=(const SHAMapItem& i) const { return mTag != i.mTag; }
bool operator==(const uint256& i) const { return mTag == i; }
bool operator!=(const uint256& i) const { return mTag != i; }
#if 0
// This code is comment out because it is unused. It could work.
bool operator<(const SHAMapItem& i) const { return mTag < i.mTag; }
bool operator>(const SHAMapItem& i) const { return mTag > i.mTag; }
bool operator<=(const SHAMapItem& i) const { return mTag <= i.mTag; }
bool operator>=(const SHAMapItem& i) const { return mTag >= i.mTag; }
bool operator<(const uint256& i) const { return mTag < i; }
bool operator>(const uint256& i) const { return mTag > i; }
bool operator<=(const uint256& i) const { return mTag <= i; }
bool operator>=(const uint256& i) const { return mTag >= i; }
#endif
virtual void dump();
};
enum SHANodeFormat
{
snfPREFIX = 1, // Form that hashes to its official hash
snfWIRE = 2, // Compressed form used on the wire
snfHASH = 3, // just the hash
};
enum SHAMapType
{
smtTRANSACTION =1, // A tree of transactions
smtSTATE =2, // A tree of state nodes
smtFREE =3, // A tree not part of a ledger
};
class SHAMapTreeNode : public SHAMapNode, public IS_INSTANCE(SHAMapTreeNode)
{
friend class SHAMap;
public:
typedef boost::shared_ptr<SHAMapTreeNode> pointer;
typedef const boost::shared_ptr<SHAMapTreeNode>& ref;
enum TNType
{
tnERROR = 0,
tnINNER = 1,
tnTRANSACTION_NM = 2, // transaction, no metadata
tnTRANSACTION_MD = 3, // transaction, with metadata
tnACCOUNT_STATE = 4
};
private:
uint256 mHash;
uint256 mHashes[16];
SHAMapItem::pointer mItem;
uint32 mSeq, mAccessSeq;
TNType mType;
int mIsBranch;
bool mFullBelow;
bool updateHash();
SHAMapTreeNode(const SHAMapTreeNode&); // no implementation
SHAMapTreeNode& operator=(const SHAMapTreeNode&); // no implementation
public:
SHAMapTreeNode(uint32 seq, const SHAMapNode& nodeID); // empty node
SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq); // copy node from older tree
SHAMapTreeNode(const SHAMapNode& nodeID, SHAMapItem::ref item, TNType type, uint32 seq);
// raw node functions
SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned char>& data, uint32 seq,
SHANodeFormat format, const uint256& hash, bool hashValid);
void addRaw(Serializer &, SHANodeFormat format);
virtual bool isPopulated() const { return true; }
// node functions
uint32 getSeq() const { return mSeq; }
void setSeq(uint32 s) { mAccessSeq = mSeq = s; }
void touch(uint32 s) { mAccessSeq = s; }
const uint256& getNodeHash() const { return mHash; }
TNType getType() const { return mType; }
// type functions
bool isLeaf() const { return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD) ||
(mType == tnACCOUNT_STATE); }
bool isInner() const { return mType == tnINNER; }
bool isValid() const { return mType != tnERROR; }
bool isTransaction() const { return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD); }
bool hasMetaData() const { return mType == tnTRANSACTION_MD; }
bool isAccountState() const { return mType == tnACCOUNT_STATE; }
// inner node functions
bool isInnerNode() const { return !mItem; }
bool setChildHash(int m, const uint256& hash);
bool isEmptyBranch(int m) const { return (mIsBranch & (1 << m)) == 0; }
bool isEmpty() const;
int getBranchCount() const;
void makeInner();
const uint256& getChildHash(int m) const
{
assert((m >= 0) && (m < 16) && (mType == tnINNER));
return mHashes[m];
}
// item node function
bool hasItem() const { return !!mItem; }
SHAMapItem::ref peekItem() { return mItem; }
SHAMapItem::pointer getItem() const;
bool setItem(SHAMapItem::ref i, TNType type);
const uint256& getTag() const { return mItem->getTag(); }
const std::vector<unsigned char>& peekData() { return mItem->peekData(); }
std::vector<unsigned char> getData() const { return mItem->getData(); }
// sync functions
bool isFullBelow(void) const { return mFullBelow; }
void setFullBelow(void) { mFullBelow = true; }
virtual void dump();
virtual std::string getString() const;
};
enum SHAMapState
{
smsModifying = 0, // Objects can be added and removed (like an open ledger)
smsImmutable = 1, // Map cannot be changed (like a closed ledger)
smsSynching = 2, // Map's hash is locked in, valid nodes can be added (like a peer's closing ledger)
smsFloating = 3, // Map is free to change hash (like a synching open ledger)
smsInvalid = 4, // Map is known not to be valid (usually synching a corrupt ledger)
};
class SHAMapSyncFilter
{
public:
SHAMapSyncFilter() { ; }
virtual ~SHAMapSyncFilter() { ; }
virtual void gotNode(bool fromFilter, const SHAMapNode& id, const uint256& nodeHash,
const std::vector<unsigned char>& nodeData, SHAMapTreeNode::TNType type)
{ ; }
virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector<unsigned char>& nodeData)
{ return false; }
};
class SHAMapMissingNode : public std::runtime_error
{
protected:
SHAMapType mType;
SHAMapNode mNodeID;
uint256 mNodeHash;
uint256 mTargetIndex;
public:
SHAMapMissingNode(SHAMapType t, const SHAMapNode& nodeID, const uint256& nodeHash) :
std::runtime_error("SHAMapMissingNode"), mType(t), mNodeID(nodeID), mNodeHash(nodeHash)
{ ; }
SHAMapMissingNode(SHAMapType t, const SHAMapNode& nodeID, const uint256& nodeHash, const uint256& targetIndex) :
std::runtime_error(nodeID.getString()), mType(t),
mNodeID(nodeID), mNodeHash(nodeHash), mTargetIndex(targetIndex)
{ ; }
virtual ~SHAMapMissingNode() throw()
{ ; }
void setTargetNode(const uint256& tn) { mTargetIndex = tn; }
SHAMapType getMapType() const { return mType; }
const SHAMapNode& getNodeID() const { return mNodeID; }
const uint256& getNodeHash() const { return mNodeHash; }
const uint256& getTargetIndex() const { return mTargetIndex; }
bool hasTargetIndex() const { return !mTargetIndex.isZero(); }
};
extern std::ostream& operator<<(std::ostream&, const SHAMapMissingNode&);
class SMAddNode
{ // results of adding nodes
protected:
bool mInvalid, mUseful;
SMAddNode(bool i, bool u) : mInvalid(i), mUseful(u) { ; }
public:
SMAddNode() : mInvalid(false), mUseful(false) { ; }
void setInvalid() { mInvalid = true; }
void setUseful() { mUseful = true; }
void reset() { mInvalid = false; mUseful = false; }
bool isInvalid() const { return mInvalid; }
bool isUseful() const { return mUseful; }
bool combine(const SMAddNode& n)
{
if (n.mInvalid)
{
mInvalid = true;
return false;
}
if (n.mUseful)
mUseful = true;
return true;
}
operator bool() const { return !mInvalid; }
static SMAddNode okay() { return SMAddNode(false, false); }
static SMAddNode useful() { return SMAddNode(false, true); }
static SMAddNode invalid() { return SMAddNode(true, false); }
};
extern bool SMANCombine(SMAddNode& existing, const SMAddNode& additional);
class SHAMap : public IS_INSTANCE(SHAMap)
{
public:
typedef boost::shared_ptr<SHAMap> pointer;
typedef const boost::shared_ptr<SHAMap>& ref;
typedef std::pair<SHAMapItem::pointer, SHAMapItem::pointer> SHAMapDiffItem;
typedef std::map<uint256, SHAMapDiffItem> SHAMapDiff;
typedef boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> SHADirtyMap;
protected:
uint32 mSeq;
uint32 mLedgerSeq; // sequence number of ledger this is part of
mutable boost::recursive_mutex mLock;
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> mTNByID;
boost::shared_ptr<SHADirtyMap> mDirtyNodes;
SHAMapTreeNode::pointer root;
SHAMapState mState;
SHAMapType mType;
static KeyCache <uint256, UptimeTimerAdapter> fullBelowCache;
void dirtyUp(std::stack<SHAMapTreeNode::pointer>& stack, const uint256& target, uint256 prevHash);
std::stack<SHAMapTreeNode::pointer> getStack(const uint256& id, bool include_nonmatching_leaf, bool partialOk);
SHAMapTreeNode::pointer walkTo(const uint256& id, bool modify);
SHAMapTreeNode* walkToPointer(const uint256& id);
SHAMapTreeNode::pointer checkCacheNode(const SHAMapNode&);
void returnNode(SHAMapTreeNode::pointer&, bool modify);
void trackNewNode(SHAMapTreeNode::pointer&);
SHAMapTreeNode::pointer getNode(const SHAMapNode& id);
SHAMapTreeNode::pointer getNode(const SHAMapNode& id, const uint256& hash, bool modify);
SHAMapTreeNode* getNodePointer(const SHAMapNode& id, const uint256& hash);
SHAMapTreeNode* getNodePointer(const SHAMapNode& id, const uint256& hash, SHAMapSyncFilter* filter);
SHAMapTreeNode* firstBelow(SHAMapTreeNode*);
SHAMapTreeNode* lastBelow(SHAMapTreeNode*);
SHAMapItem::pointer onlyBelow(SHAMapTreeNode*);
void eraseChildren(SHAMapTreeNode::pointer);
void dropBelow(SHAMapTreeNode*);
bool hasInnerNode(const SHAMapNode& nodeID, const uint256& hash);
bool hasLeafNode(const uint256& tag, const uint256& hash);
bool walkBranch(SHAMapTreeNode* node, SHAMapItem::ref otherMapItem, bool isFirstMap,
SHAMapDiff& differences, int& maxCount);
public:
// build new map
SHAMap(SHAMapType t, uint32 seq = 1);
SHAMap(SHAMapType t, const uint256& hash);
~SHAMap() { mState = smsInvalid; }
// Returns a new map that's a snapshot of this one. Force CoW
SHAMap::pointer snapShot(bool isMutable);
// Remove nodes from memory
void dropCache();
void setLedgerSeq(uint32 lseq) { mLedgerSeq = lseq; }
// hold the map stable across operations
ScopedLock Lock() const { return ScopedLock(mLock); }
bool hasNode(const SHAMapNode& id);
void fetchRoot(const uint256& hash, SHAMapSyncFilter* filter);
// normal hash access functions
bool hasItem(const uint256& id);
bool delItem(const uint256& id);
bool addItem(const SHAMapItem& i, bool isTransaction, bool hasMeta);
bool updateItem(const SHAMapItem& i, bool isTransaction, bool hasMeta);
SHAMapItem getItem(const uint256& id);
uint256 getHash() const { return root->getNodeHash(); }
uint256 getHash() { return root->getNodeHash(); }
// save a copy if you have a temporary anyway
bool updateGiveItem(SHAMapItem::ref, bool isTransaction, bool hasMeta);
bool addGiveItem(SHAMapItem::ref, bool isTransaction, bool hasMeta);
// save a copy if you only need a temporary
SHAMapItem::pointer peekItem(const uint256& id);
SHAMapItem::pointer peekItem(const uint256& id, uint256& hash);
SHAMapItem::pointer peekItem(const uint256& id, SHAMapTreeNode::TNType& type);
// traverse functions
SHAMapItem::pointer peekFirstItem();
SHAMapItem::pointer peekFirstItem(SHAMapTreeNode::TNType& type);
SHAMapItem::pointer peekLastItem();
SHAMapItem::pointer peekNextItem(const uint256&);
SHAMapItem::pointer peekNextItem(const uint256&, SHAMapTreeNode::TNType& type);
SHAMapItem::pointer peekPrevItem(const uint256&);
// comparison/sync functions
void getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max,
SHAMapSyncFilter* filter);
bool getNodeFat(const SHAMapNode& node, std::vector<SHAMapNode>& nodeIDs,
std::list<std::vector<unsigned char> >& rawNode, bool fatRoot, bool fatLeaves);
bool getRootNode(Serializer& s, SHANodeFormat format);
std::vector<uint256> getNeededHashes(int max, SHAMapSyncFilter* filter);
SMAddNode addRootNode(const uint256& hash, const std::vector<unsigned char>& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter);
SMAddNode addRootNode(const std::vector<unsigned char>& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter);
SMAddNode addKnownNode(const SHAMapNode& nodeID, const std::vector<unsigned char>& rawNode,
SHAMapSyncFilter* filter);
// status functions
void setImmutable() { assert(mState != smsInvalid); mState = smsImmutable; }
void clearImmutable() { mState = smsModifying; }
bool isSynching() const { return (mState == smsFloating) || (mState == smsSynching); }
void setSynching() { mState = smsSynching; }
void setFloating() { mState = smsFloating; }
void clearSynching() { mState = smsModifying; }
bool isValid() { return mState != smsInvalid; }
// caution: otherMap must be accessed only by this function
// return value: true=successfully completed, false=too different
bool compare(SHAMap::ref otherMap, SHAMapDiff& differences, int maxCount);
int armDirty();
static int flushDirty(SHADirtyMap& dirtyMap, int maxNodes, HashedObjectType t, uint32 seq);
boost::shared_ptr<SHADirtyMap> disarmDirty();
void setSeq(uint32 seq) { mSeq = seq; }
uint32 getSeq() { return mSeq; }
// overloads for backed maps
boost::shared_ptr<SHAMapTreeNode> fetchNodeExternal(const SHAMapNode& id, const uint256& hash);
bool operator==(const SHAMap& s) { return getHash() == s.getHash(); }
// trusted path operations - prove a particular node is in a particular ledger
std::list<std::vector<unsigned char> > getTrustedPath(const uint256& index);
static std::vector<unsigned char> checkTrustedPath(const uint256& ledgerHash, const uint256& leafIndex,
const std::list<std::vector<unsigned char> >& path);
void walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing);
bool getPath(const uint256& index, std::vector< std::vector<unsigned char> >& nodes, SHANodeFormat format);
bool deepCompare(SHAMap& other);
virtual void dump(bool withHashes = false);
typedef std::pair< uint256, std::vector<unsigned char> > fetchPackEntry_t;
std::list<fetchPackEntry_t> getFetchPack(SHAMap* have, bool includeLeaves, int max);
static void sweep() { fullBelowCache.sweep(); }
};
#endif
// vim:ts=4