Jianping Wu
6 years ago
13 changed files with 1082 additions and 94 deletions
@ -0,0 +1,295 @@
@@ -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 @@
@@ -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