Jianping Wu
6 years ago
13 changed files with 1082 additions and 94 deletions
@ -0,0 +1,295 @@ |
|||||||
|
// Copyright (c) 2014-2017 Daniel Kraft
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
// Copyright (c) 2018 Jianping Wu
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <keva/common.h> |
||||||
|
|
||||||
|
#include <script/keva.h> |
||||||
|
|
||||||
|
bool fNameHistory = false; |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CKevaData. */ |
||||||
|
|
||||||
|
void |
||||||
|
CKevaData::fromScript (unsigned h, const COutPoint& out, |
||||||
|
const CKevaScript& script) |
||||||
|
{ |
||||||
|
assert (script.isAnyUpdate ()); |
||||||
|
value = script.getOpValue (); |
||||||
|
nHeight = h; |
||||||
|
prevout = out; |
||||||
|
addr = script.getAddress (); |
||||||
|
} |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CNameIterator. */ |
||||||
|
|
||||||
|
CNameIterator::~CNameIterator () |
||||||
|
{ |
||||||
|
/* Nothing to be done here. This may be overwritten by
|
||||||
|
subclasses if they need a destructor. */ |
||||||
|
} |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CNameCacheNameIterator. */ |
||||||
|
|
||||||
|
class CCacheNameIterator : public CNameIterator |
||||||
|
{ |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
/** Reference to cache object that is used. */ |
||||||
|
const CNameCache& cache; |
||||||
|
|
||||||
|
/** Base iterator to combine with the cache. */ |
||||||
|
CNameIterator* base; |
||||||
|
|
||||||
|
/** Whether or not the base iterator has more entries. */ |
||||||
|
bool baseHasMore; |
||||||
|
/** "Next" name of the base iterator. */ |
||||||
|
valtype baseName; |
||||||
|
/** "Next" data of the base iterator. */ |
||||||
|
CKevaData baseData; |
||||||
|
|
||||||
|
/** Iterator of the cache's entries. */ |
||||||
|
CNameCache::EntryMap::const_iterator cacheIter; |
||||||
|
|
||||||
|
/* Call the base iterator's next() routine to fill in the internal
|
||||||
|
"cache" for the next entry. This already skips entries that are |
||||||
|
marked as deleted in the cache. */ |
||||||
|
void advanceBaseIterator (); |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the iterator. This takes ownership of the base iterator. |
||||||
|
* @param c The cache object to use. |
||||||
|
* @param b The base iterator. |
||||||
|
*/ |
||||||
|
CCacheNameIterator (const CNameCache& c, CNameIterator* b); |
||||||
|
|
||||||
|
/* Destruct, this deletes also the base iterator. */ |
||||||
|
~CCacheNameIterator (); |
||||||
|
|
||||||
|
/* Implement iterator methods. */ |
||||||
|
void seek (const valtype& name); |
||||||
|
bool next (valtype& name, CKevaData& data); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
CCacheNameIterator::CCacheNameIterator (const CNameCache& c, CNameIterator* b) |
||||||
|
: cache(c), base(b) |
||||||
|
{ |
||||||
|
/* Add a seek-to-start to ensure that everything is consistent. This call
|
||||||
|
may be superfluous if we seek to another position afterwards anyway, |
||||||
|
but it should also not hurt too much. */ |
||||||
|
seek (valtype ()); |
||||||
|
} |
||||||
|
|
||||||
|
CCacheNameIterator::~CCacheNameIterator () |
||||||
|
{ |
||||||
|
delete base; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CCacheNameIterator::advanceBaseIterator () |
||||||
|
{ |
||||||
|
assert (baseHasMore); |
||||||
|
do |
||||||
|
baseHasMore = base->next (baseName, baseData); |
||||||
|
while (baseHasMore && cache.isDeleted (baseName)); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CCacheNameIterator::seek (const valtype& start) |
||||||
|
{ |
||||||
|
cacheIter = cache.entries.lower_bound (start); |
||||||
|
base->seek (start); |
||||||
|
|
||||||
|
baseHasMore = true; |
||||||
|
advanceBaseIterator (); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
CCacheNameIterator::next (valtype& name, CKevaData& data) |
||||||
|
{ |
||||||
|
/* Exit early if no more data is available in either the cache
|
||||||
|
nor the base iterator. */ |
||||||
|
if (!baseHasMore && cacheIter == cache.entries.end ()) |
||||||
|
return false; |
||||||
|
|
||||||
|
/* Determine which source to use for the next. */ |
||||||
|
bool useBase; |
||||||
|
if (!baseHasMore) |
||||||
|
useBase = false; |
||||||
|
else if (cacheIter == cache.entries.end ()) |
||||||
|
useBase = true; |
||||||
|
else |
||||||
|
{ |
||||||
|
/* A special case is when both iterators are equal. In this case,
|
||||||
|
we want to use the cached version. We also have to advance |
||||||
|
the base iterator. */ |
||||||
|
if (baseName == cacheIter->first) |
||||||
|
advanceBaseIterator (); |
||||||
|
|
||||||
|
/* Due to advancing the base iterator above, it may happen that
|
||||||
|
no more base entries are present. Handle this gracefully. */ |
||||||
|
if (!baseHasMore) |
||||||
|
useBase = false; |
||||||
|
else |
||||||
|
{ |
||||||
|
assert (baseName != cacheIter->first); |
||||||
|
|
||||||
|
CNameCache::NameComparator cmp; |
||||||
|
useBase = cmp (baseName, cacheIter->first); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Use the correct source now and advance it. */ |
||||||
|
if (useBase) |
||||||
|
{ |
||||||
|
name = baseName; |
||||||
|
data = baseData; |
||||||
|
advanceBaseIterator (); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
name = cacheIter->first; |
||||||
|
data = cacheIter->second; |
||||||
|
++cacheIter; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CNameCache. */ |
||||||
|
|
||||||
|
bool |
||||||
|
CNameCache::get (const valtype& name, CKevaData& data) const |
||||||
|
{ |
||||||
|
const EntryMap::const_iterator i = entries.find (name); |
||||||
|
if (i == entries.end ()) |
||||||
|
return false; |
||||||
|
|
||||||
|
data = i->second; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CNameCache::set (const valtype& name, const CKevaData& data) |
||||||
|
{ |
||||||
|
const std::set<valtype>::iterator di = deleted.find (name); |
||||||
|
if (di != deleted.end ()) |
||||||
|
deleted.erase (di); |
||||||
|
|
||||||
|
const EntryMap::iterator ei = entries.find (name); |
||||||
|
if (ei != entries.end ()) |
||||||
|
ei->second = data; |
||||||
|
else |
||||||
|
entries.insert (std::make_pair (name, data)); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CNameCache::remove (const valtype& name) |
||||||
|
{ |
||||||
|
const EntryMap::iterator ei = entries.find (name); |
||||||
|
if (ei != entries.end ()) |
||||||
|
entries.erase (ei); |
||||||
|
|
||||||
|
deleted.insert (name); |
||||||
|
} |
||||||
|
|
||||||
|
CNameIterator* |
||||||
|
CNameCache::iterateNames (CNameIterator* base) const |
||||||
|
{ |
||||||
|
return new CCacheNameIterator (*this, base); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
CNameCache::getHistory (const valtype& name, CNameHistory& res) const |
||||||
|
{ |
||||||
|
assert (fNameHistory); |
||||||
|
|
||||||
|
const std::map<valtype, CNameHistory>::const_iterator i = history.find (name); |
||||||
|
if (i == history.end ()) |
||||||
|
return false; |
||||||
|
|
||||||
|
res = i->second; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CNameCache::setHistory (const valtype& name, const CNameHistory& data) |
||||||
|
{ |
||||||
|
assert (fNameHistory); |
||||||
|
|
||||||
|
const std::map<valtype, CNameHistory>::iterator ei = history.find (name); |
||||||
|
if (ei != history.end ()) |
||||||
|
ei->second = data; |
||||||
|
else |
||||||
|
history.insert (std::make_pair (name, data)); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CNameCache::updateNamesForHeight (unsigned nHeight, |
||||||
|
std::set<valtype>& names) const |
||||||
|
{ |
||||||
|
/* Seek in the map of cached entries to the first one corresponding
|
||||||
|
to our height. */ |
||||||
|
|
||||||
|
const ExpireEntry seekEntry(nHeight, valtype ()); |
||||||
|
std::map<ExpireEntry, bool>::const_iterator it; |
||||||
|
|
||||||
|
for (it = expireIndex.lower_bound (seekEntry); it != expireIndex.end (); ++it) |
||||||
|
{ |
||||||
|
const ExpireEntry& cur = it->first; |
||||||
|
assert (cur.nHeight >= nHeight); |
||||||
|
if (cur.nHeight > nHeight) |
||||||
|
break; |
||||||
|
|
||||||
|
if (it->second) |
||||||
|
names.insert (cur.name); |
||||||
|
else |
||||||
|
names.erase (cur.name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CNameCache::addExpireIndex (const valtype& name, unsigned height) |
||||||
|
{ |
||||||
|
const ExpireEntry entry(height, name); |
||||||
|
expireIndex[entry] = true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CNameCache::removeExpireIndex (const valtype& name, unsigned height) |
||||||
|
{ |
||||||
|
const ExpireEntry entry(height, name); |
||||||
|
expireIndex[entry] = false; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
CNameCache::apply (const CNameCache& cache) |
||||||
|
{ |
||||||
|
for (EntryMap::const_iterator i = cache.entries.begin (); |
||||||
|
i != cache.entries.end (); ++i) |
||||||
|
set (i->first, i->second); |
||||||
|
|
||||||
|
for (std::set<valtype>::const_iterator i = cache.deleted.begin (); |
||||||
|
i != cache.deleted.end (); ++i) |
||||||
|
remove (*i); |
||||||
|
|
||||||
|
for (std::map<valtype, CNameHistory>::const_iterator i |
||||||
|
= cache.history.begin (); i != cache.history.end (); ++i) |
||||||
|
setHistory (i->first, i->second); |
||||||
|
|
||||||
|
for (std::map<ExpireEntry, bool>::const_iterator i |
||||||
|
= cache.expireIndex.begin (); i != cache.expireIndex.end (); ++i) |
||||||
|
expireIndex[i->first] = i->second; |
||||||
|
} |
@ -0,0 +1,485 @@ |
|||||||
|
// Copyright (c) 2014-2017 Daniel Kraft
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
// Copyright (c) 2018 Jianping Wu
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef H_BITCOIN_NAMES_COMMON |
||||||
|
#define H_BITCOIN_NAMES_COMMON |
||||||
|
|
||||||
|
#include <compat/endian.h> |
||||||
|
#include <primitives/transaction.h> |
||||||
|
#include <script/script.h> |
||||||
|
#include <serialize.h> |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <set> |
||||||
|
|
||||||
|
class CKevaScript; |
||||||
|
class CDBBatch; |
||||||
|
|
||||||
|
/** Whether or not name history is enabled. */ |
||||||
|
extern bool fNameHistory; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a valtype (e. g., name) from a string. |
||||||
|
* @param str The string input. |
||||||
|
* @return The corresponding valtype. |
||||||
|
*/ |
||||||
|
inline valtype |
||||||
|
ValtypeFromString (const std::string& str) |
||||||
|
{ |
||||||
|
return valtype (str.begin (), str.end ()); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a valtype to a string. |
||||||
|
* @param val The valtype value. |
||||||
|
* @return Corresponding string. |
||||||
|
*/ |
||||||
|
inline std::string |
||||||
|
ValtypeToString (const valtype& val) |
||||||
|
{ |
||||||
|
return std::string (val.begin (), val.end ()); |
||||||
|
} |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CKevaData. */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Information stored for a name in the database. |
||||||
|
*/ |
||||||
|
class CKevaData |
||||||
|
{ |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
/** The name's value. */ |
||||||
|
valtype value; |
||||||
|
|
||||||
|
/** The transaction's height. Used for expiry. */ |
||||||
|
unsigned nHeight; |
||||||
|
|
||||||
|
/** The name's last update outpoint. */ |
||||||
|
COutPoint prevout; |
||||||
|
|
||||||
|
/**
|
||||||
|
* The name's address (as script). This is kept here also, because |
||||||
|
* that information is useful to extract on demand (e. g., in name_show). |
||||||
|
*/ |
||||||
|
CScript addr; |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS; |
||||||
|
|
||||||
|
template<typename Stream, typename Operation> |
||||||
|
inline void SerializationOp (Stream& s, Operation ser_action) |
||||||
|
{ |
||||||
|
READWRITE (value); |
||||||
|
READWRITE (nHeight); |
||||||
|
READWRITE (prevout); |
||||||
|
READWRITE (*(CScriptBase*)(&addr)); |
||||||
|
} |
||||||
|
|
||||||
|
/* Compare for equality. */ |
||||||
|
friend inline bool |
||||||
|
operator== (const CKevaData& a, const CKevaData& b) |
||||||
|
{ |
||||||
|
return a.value == b.value && a.nHeight == b.nHeight |
||||||
|
&& a.prevout == b.prevout && a.addr == b.addr; |
||||||
|
} |
||||||
|
friend inline bool |
||||||
|
operator!= (const CKevaData& a, const CKevaData& b) |
||||||
|
{ |
||||||
|
return !(a == b); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the height. |
||||||
|
* @return The name's update height. |
||||||
|
*/ |
||||||
|
inline unsigned |
||||||
|
getHeight () const |
||||||
|
{ |
||||||
|
return nHeight; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value. |
||||||
|
* @return The name's value. |
||||||
|
*/ |
||||||
|
inline const valtype& |
||||||
|
getValue () const |
||||||
|
{ |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name's update outpoint. |
||||||
|
* @return The update outpoint. |
||||||
|
*/ |
||||||
|
inline const COutPoint& |
||||||
|
getUpdateOutpoint () const |
||||||
|
{ |
||||||
|
return prevout; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the address. |
||||||
|
* @return The name's address. |
||||||
|
*/ |
||||||
|
inline const CScript& |
||||||
|
getAddress () const |
||||||
|
{ |
||||||
|
return addr; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the name is expired at the current chain height. |
||||||
|
* @return True iff the name is expired. |
||||||
|
*/ |
||||||
|
bool isExpired () const; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the name is expired at the given height. |
||||||
|
* @param h The height at which to check. |
||||||
|
* @return True iff the name is expired at height h. |
||||||
|
*/ |
||||||
|
bool isExpired (unsigned h) const; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Set from a name update operation. |
||||||
|
* @param h The height (not available from script). |
||||||
|
* @param out The update outpoint. |
||||||
|
* @param script The name script. Should be a name (first) update. |
||||||
|
*/ |
||||||
|
void fromScript (unsigned h, const COutPoint& out, const CKevaScript& script); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CNameHistory. */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep track of a name's history. This is a stack of old CKevaData |
||||||
|
* objects that have been obsoleted. |
||||||
|
*/ |
||||||
|
class CNameHistory |
||||||
|
{ |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
/** The actual data. */ |
||||||
|
std::vector<CKevaData> data; |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS; |
||||||
|
|
||||||
|
template<typename Stream, typename Operation> |
||||||
|
inline void SerializationOp (Stream& s, Operation ser_action) |
||||||
|
{ |
||||||
|
READWRITE (data); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the stack is empty. This is used to decide when to fully |
||||||
|
* delete an entry in the database. |
||||||
|
* @return True iff the data stack is empty. |
||||||
|
*/ |
||||||
|
inline bool |
||||||
|
empty () const |
||||||
|
{ |
||||||
|
return data.empty (); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the data in a read-only way. |
||||||
|
* @return The data stack. |
||||||
|
*/ |
||||||
|
inline const std::vector<CKevaData>& |
||||||
|
getData () const |
||||||
|
{ |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Push a new entry onto the data stack. The new entry's height should |
||||||
|
* be at least as high as the stack top entry's. If not, fail. |
||||||
|
* @param entry The new entry to push onto the stack. |
||||||
|
*/ |
||||||
|
inline void |
||||||
|
push (const CKevaData& entry) |
||||||
|
{ |
||||||
|
assert (data.empty () || data.back ().getHeight () <= entry.getHeight ()); |
||||||
|
data.push_back (entry); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Pop the top entry off the stack. This is used when undoing name |
||||||
|
* changes. The name's new value is passed as argument and should |
||||||
|
* match the removed entry. If not, fail. |
||||||
|
* @param entry The name's value after undoing. |
||||||
|
*/ |
||||||
|
inline void |
||||||
|
pop (const CKevaData& entry) |
||||||
|
{ |
||||||
|
assert (!data.empty () && data.back () == entry); |
||||||
|
data.pop_back (); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CNameIterator. */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for iterators over the name database. |
||||||
|
*/ |
||||||
|
class CNameIterator |
||||||
|
{ |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
// Virtual destructor in case subclasses need them.
|
||||||
|
virtual ~CNameIterator (); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek to a given lower bound. |
||||||
|
* @param start The name to seek to. |
||||||
|
*/ |
||||||
|
virtual void seek (const valtype& name) = 0; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next name. Returns false if no more names are available. |
||||||
|
* @param name Put the name here. |
||||||
|
* @param data Put the name's data here. |
||||||
|
* @return True if successful, false if no more names. |
||||||
|
*/ |
||||||
|
virtual bool next (valtype& name, CKevaData& data) = 0; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
/* ************************************************************************** */ |
||||||
|
/* CNameCache. */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache / record of updates to the name database. In addition to |
||||||
|
* new names (or updates to them), this also keeps track of deleted names |
||||||
|
* (when rolling back changes). |
||||||
|
*/ |
||||||
|
class CNameCache |
||||||
|
{ |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
/**
|
||||||
|
* Special comparator class for names that compares by length first. |
||||||
|
* This is used to sort the cache entry map in the same way as the |
||||||
|
* database is sorted. |
||||||
|
*/ |
||||||
|
class NameComparator |
||||||
|
{ |
||||||
|
public: |
||||||
|
inline bool |
||||||
|
operator() (const valtype& a, const valtype& b) const |
||||||
|
{ |
||||||
|
if (a.size () != b.size ()) |
||||||
|
return a.size () < b.size (); |
||||||
|
|
||||||
|
return a < b; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for expire-index entries. We have to make sure that |
||||||
|
* it is serialised in such a way that ordering is done correctly |
||||||
|
* by height. This is not true if we use a std::pair, since then |
||||||
|
* the height is serialised as byte-array with little-endian order, |
||||||
|
* which does not correspond to the ordering by actual value. |
||||||
|
*/ |
||||||
|
class ExpireEntry |
||||||
|
{ |
||||||
|
public: |
||||||
|
|
||||||
|
unsigned nHeight; |
||||||
|
valtype name; |
||||||
|
|
||||||
|
inline ExpireEntry () |
||||||
|
: nHeight(0), name() |
||||||
|
{} |
||||||
|
|
||||||
|
inline ExpireEntry (unsigned h, const valtype& n) |
||||||
|
: nHeight(h), name(n) |
||||||
|
{} |
||||||
|
|
||||||
|
/* Default copy and assignment. */ |
||||||
|
|
||||||
|
template<typename Stream> |
||||||
|
inline void |
||||||
|
Serialize (Stream& s) const |
||||||
|
{ |
||||||
|
/* Flip the byte order of nHeight to big endian. */ |
||||||
|
const uint32_t nHeightFlipped = htobe32 (nHeight); |
||||||
|
|
||||||
|
::Serialize (s, nHeightFlipped); |
||||||
|
::Serialize (s, name); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename Stream> |
||||||
|
inline void |
||||||
|
Unserialize (Stream& s) |
||||||
|
{ |
||||||
|
uint32_t nHeightFlipped; |
||||||
|
|
||||||
|
::Unserialize (s, nHeightFlipped); |
||||||
|
::Unserialize (s, name); |
||||||
|
|
||||||
|
/* Unflip the byte order. */ |
||||||
|
nHeight = be32toh (nHeightFlipped); |
||||||
|
} |
||||||
|
|
||||||
|
friend inline bool |
||||||
|
operator== (const ExpireEntry& a, const ExpireEntry& b) |
||||||
|
{ |
||||||
|
return a.nHeight == b.nHeight && a.name == b.name; |
||||||
|
} |
||||||
|
|
||||||
|
friend inline bool |
||||||
|
operator!= (const ExpireEntry& a, const ExpireEntry& b) |
||||||
|
{ |
||||||
|
return !(a == b); |
||||||
|
} |
||||||
|
|
||||||
|
friend inline bool |
||||||
|
operator< (const ExpireEntry& a, const ExpireEntry& b) |
||||||
|
{ |
||||||
|
if (a.nHeight != b.nHeight) |
||||||
|
return a.nHeight < b.nHeight; |
||||||
|
|
||||||
|
return a.name < b.name; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of name entry map. This is public because it is also used |
||||||
|
* by the unit tests. |
||||||
|
*/ |
||||||
|
typedef std::map<valtype, CKevaData, NameComparator> EntryMap; |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
/** New or updated names. */ |
||||||
|
EntryMap entries; |
||||||
|
/** Deleted names. */ |
||||||
|
std::set<valtype> deleted; |
||||||
|
|
||||||
|
/**
|
||||||
|
* New or updated history stacks. If they are empty, the corresponding |
||||||
|
* database entry is deleted instead. |
||||||
|
*/ |
||||||
|
std::map<valtype, CNameHistory> history; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes to be performed to the expire index. The entry is mapped |
||||||
|
* to either "true" (meaning to add it) or "false" (delete). |
||||||
|
*/ |
||||||
|
std::map<ExpireEntry, bool> expireIndex; |
||||||
|
|
||||||
|
friend class CCacheNameIterator; |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
inline void |
||||||
|
clear () |
||||||
|
{ |
||||||
|
entries.clear (); |
||||||
|
deleted.clear (); |
||||||
|
history.clear (); |
||||||
|
expireIndex.clear (); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the cache is "clean" (no cached changes). This also |
||||||
|
* performs internal checks and fails with an assertion if the |
||||||
|
* internal state is inconsistent. |
||||||
|
* @return True iff no changes are cached. |
||||||
|
*/ |
||||||
|
inline bool |
||||||
|
empty () const |
||||||
|
{ |
||||||
|
if (entries.empty () && deleted.empty ()) |
||||||
|
{ |
||||||
|
assert (history.empty () && expireIndex.empty ()); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/* See if the given name is marked as deleted. */ |
||||||
|
inline bool |
||||||
|
isDeleted (const valtype& name) const |
||||||
|
{ |
||||||
|
return (deleted.count (name) > 0); |
||||||
|
} |
||||||
|
|
||||||
|
/* Try to get a name's associated data. This looks only
|
||||||
|
in entries, and doesn't care about deleted data. */ |
||||||
|
bool get (const valtype& name, CKevaData& data) const; |
||||||
|
|
||||||
|
/* Insert (or update) a name. If it is marked as "deleted", this also
|
||||||
|
removes the "deleted" mark. */ |
||||||
|
void set (const valtype& name, const CKevaData& data); |
||||||
|
|
||||||
|
/* Delete a name. If it is in the "entries" set also, remove it there. */ |
||||||
|
void remove (const valtype& name); |
||||||
|
|
||||||
|
/* Return a name iterator that combines a "base" iterator with the changes
|
||||||
|
made to it according to the cache. The base iterator is taken |
||||||
|
ownership of. */ |
||||||
|
CNameIterator* iterateNames (CNameIterator* base) const; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for an history entry. |
||||||
|
* @param name The name to look up. |
||||||
|
* @param res Put the resulting history entry here. |
||||||
|
* @return True iff the name was found in the cache. |
||||||
|
*/ |
||||||
|
bool getHistory (const valtype& name, CNameHistory& res) const; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a name history entry. |
||||||
|
* @param name The name to modify. |
||||||
|
* @param data The new history entry. |
||||||
|
*/ |
||||||
|
void setHistory (const valtype& name, const CNameHistory& data); |
||||||
|
|
||||||
|
/* Query the cached changes to the expire index. In particular,
|
||||||
|
for a given height and a given set of names that were indexed to |
||||||
|
this update height, apply possible changes to the set that |
||||||
|
are represented by the cached expire index changes. */ |
||||||
|
void updateNamesForHeight (unsigned nHeight, std::set<valtype>& names) const; |
||||||
|
|
||||||
|
/* Add an expire-index entry. */ |
||||||
|
void addExpireIndex (const valtype& name, unsigned height); |
||||||
|
|
||||||
|
/* Remove an expire-index entry. */ |
||||||
|
void removeExpireIndex (const valtype& name, unsigned height); |
||||||
|
|
||||||
|
/* Apply all the changes in the passed-in record on top of this one. */ |
||||||
|
void apply (const CNameCache& cache); |
||||||
|
|
||||||
|
/* Write all cached changes to a database batch update object. */ |
||||||
|
void writeBatch (CDBBatch& batch) const; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // H_BITCOIN_NAMES_COMMON
|
Loading…
Reference in new issue