mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-03-12 13:41:52 +00:00
WIP: started implementing keva_namespace.
This commit is contained in:
parent
d87c7b8eea
commit
af231f9ec4
295
src/keva/common.cpp
Normal file
295
src/keva/common.cpp
Normal file
@ -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
|
@ -1,3 +1,7 @@
|
|||||||
|
// 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
|
// Copyright (c) 2018 Jianping Wu
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
@ -19,29 +23,29 @@
|
|||||||
|
|
||||||
|
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
/* CNameTxUndo. */
|
/* CKevaTxUndo. */
|
||||||
|
|
||||||
void
|
void
|
||||||
CNameTxUndo::fromOldState (const valtype& nm, const CCoinsView& view)
|
CKevaTxUndo::fromOldState(const valtype& nm, const CCoinsView& view)
|
||||||
{
|
{
|
||||||
name = nm;
|
name = nm;
|
||||||
isNew = !view.GetName (name, oldData);
|
isNew = !view.GetName(name, oldData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CNameTxUndo::apply (CCoinsViewCache& view) const
|
CKevaTxUndo::apply (CCoinsViewCache& view) const
|
||||||
{
|
{
|
||||||
if (isNew)
|
if (isNew)
|
||||||
view.DeleteName (name);
|
view.DeleteName(name);
|
||||||
else
|
else
|
||||||
view.SetName (name, oldData, true);
|
view.SetName(name, oldData, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
/* CKevaMemPool. */
|
/* CKevaMemPool. */
|
||||||
|
|
||||||
uint256
|
uint256
|
||||||
CKevaMemPool::getTxForName (const valtype& name) const
|
CKevaMemPool::getTxForName(const valtype& name) const
|
||||||
{
|
{
|
||||||
NameTxMap::const_iterator mi;
|
NameTxMap::const_iterator mi;
|
||||||
|
|
||||||
@ -536,7 +540,7 @@ ApplyNameTransaction (const CTransaction& tx, unsigned nHeight,
|
|||||||
LogPrint (BCLog::NAMES, "Updating name at height %d: %s\n",
|
LogPrint (BCLog::NAMES, "Updating name at height %d: %s\n",
|
||||||
nHeight, ValtypeToString (name).c_str ());
|
nHeight, ValtypeToString (name).c_str ());
|
||||||
|
|
||||||
CNameTxUndo opUndo;
|
CKevaTxUndo opUndo;
|
||||||
opUndo.fromOldState (name, view);
|
opUndo.fromOldState (name, view);
|
||||||
undo.vnameundo.push_back (opUndo);
|
undo.vnameundo.push_back (opUndo);
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
// 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
|
// Copyright (c) 2018 Jianping Wu
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
@ -33,14 +37,14 @@ static const unsigned MAX_VALUE_LENGTH = 1023;
|
|||||||
static const CAmount KEVA_LOCKED_AMOUNT = COIN / 100;
|
static const CAmount KEVA_LOCKED_AMOUNT = COIN / 100;
|
||||||
|
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
/* CNameTxUndo. */
|
/* CKevaTxUndo. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo information for one name operation. This contains either the
|
* Undo information for one name operation. This contains either the
|
||||||
* information that the name was newly created (and should thus be
|
* information that the name was newly created (and should thus be
|
||||||
* deleted entirely) or that it was updated including the old value.
|
* deleted entirely) or that it was updated including the old value.
|
||||||
*/
|
*/
|
||||||
class CNameTxUndo
|
class CKevaTxUndo
|
||||||
{
|
{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -441,11 +441,14 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
|
|||||||
if(!g_connman)
|
if(!g_connman)
|
||||||
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
|
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
|
||||||
|
|
||||||
|
/*
|
||||||
|
// JWU disable for now!!!
|
||||||
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
|
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
|
||||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Litecoin is not connected!");
|
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Litecoin is not connected!");
|
||||||
|
|
||||||
if (IsInitialBlockDownload())
|
if (IsInitialBlockDownload())
|
||||||
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Litecoin is downloading blocks...");
|
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Litecoin is downloading blocks...");
|
||||||
|
*/
|
||||||
|
|
||||||
static unsigned int nTransactionsUpdatedLast;
|
static unsigned int nTransactionsUpdatedLast;
|
||||||
|
|
||||||
|
@ -15,39 +15,48 @@ CKevaScript::CKevaScript (const CScript& script)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
opcodetype opcode;
|
opcodetype opcode;
|
||||||
while (true)
|
while (true) {
|
||||||
{
|
valtype vch;
|
||||||
valtype vch;
|
|
||||||
|
|
||||||
if (!script.GetOp (pc, opcode, vch))
|
if (!script.GetOp (pc, opcode, vch)) {
|
||||||
return;
|
return;
|
||||||
if (opcode == OP_DROP || opcode == OP_2DROP || opcode == OP_NOP)
|
}
|
||||||
break;
|
if (opcode == OP_DROP || opcode == OP_2DROP || opcode == OP_NOP) {
|
||||||
if (!(opcode >= 0 && opcode <= OP_PUSHDATA4))
|
break;
|
||||||
return;
|
}
|
||||||
|
if (!(opcode >= 0 && opcode <= OP_PUSHDATA4)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
args.push_back (vch);
|
args.push_back (vch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the pc to after any DROP or NOP.
|
// Move the pc to after any DROP or NOP.
|
||||||
while (opcode == OP_DROP || opcode == OP_2DROP || opcode == OP_NOP)
|
while (opcode == OP_DROP || opcode == OP_2DROP || opcode == OP_NOP) {
|
||||||
if (!script.GetOp (pc, opcode))
|
if (!script.GetOp (pc, opcode))
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
pc--;
|
pc--;
|
||||||
|
|
||||||
/* Now, we have the args and the operation. Check if we have indeed
|
/* Now, we have the args and the operation. Check if we have indeed
|
||||||
a valid name operation and valid argument counts. Only now set the
|
a valid name operation and valid argument counts. Only now set the
|
||||||
op and address members, if everything is valid. */
|
op and address members, if everything is valid. */
|
||||||
switch (nameOp)
|
switch (nameOp) {
|
||||||
{
|
|
||||||
case OP_KEVA_PUT:
|
case OP_KEVA_PUT:
|
||||||
if (args.size () != 1)
|
if (args.size () != 1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_KEVA_NAMESPACE:
|
||||||
|
if (args.size () != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
op = nameOp;
|
op = nameOp;
|
||||||
address = CScript (pc, script.end ());
|
address = CScript (pc, script.end ());
|
||||||
@ -58,8 +67,7 @@ CKevaScript::buildKevaPut(const CScript& addr, const valtype& nameSpace,
|
|||||||
const valtype& key, const valtype& value)
|
const valtype& key, const valtype& value)
|
||||||
{
|
{
|
||||||
CScript prefix;
|
CScript prefix;
|
||||||
prefix << OP_KEVA_PUT << nameSpace << key << value
|
prefix << OP_KEVA_PUT << nameSpace << key << value << OP_2DROP << OP_DROP;
|
||||||
<< OP_2DROP << OP_2DROP;
|
|
||||||
|
|
||||||
return prefix + addr;
|
return prefix + addr;
|
||||||
}
|
}
|
||||||
@ -68,8 +76,7 @@ CScript CKevaScript::buildKevaNamespace(const CScript& addr, const valtype& name
|
|||||||
const valtype& displayName)
|
const valtype& displayName)
|
||||||
{
|
{
|
||||||
CScript prefix;
|
CScript prefix;
|
||||||
prefix << OP_KEVA_NAMESPACE << nameSpace << displayName
|
prefix << OP_KEVA_NAMESPACE << nameSpace << displayName << OP_2DROP;
|
||||||
<< OP_2DROP << OP_2DROP;
|
|
||||||
|
|
||||||
return prefix + addr;
|
return prefix + addr;
|
||||||
}
|
}
|
||||||
|
@ -52,25 +52,26 @@ public:
|
|||||||
inline bool
|
inline bool
|
||||||
isNameOp () const
|
isNameOp () const
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op) {
|
||||||
{
|
case OP_KEVA_PUT:
|
||||||
case OP_KEVA_PUT:
|
return true;
|
||||||
|
|
||||||
|
case OP_KEVA_NAMESPACE:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case OP_NOP:
|
case OP_NOP:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert (false);
|
assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the non-name script.
|
* Return the non-name script.
|
||||||
* @return The address part.
|
* @return The address part.
|
||||||
*/
|
*/
|
||||||
inline const CScript&
|
inline const CScript& getAddress() const
|
||||||
getAddress () const
|
|
||||||
{
|
{
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
@ -80,17 +81,18 @@ public:
|
|||||||
* or OP_NAME_UPDATE. Do not call if this is not a name script.
|
* or OP_NAME_UPDATE. Do not call if this is not a name script.
|
||||||
* @return The name operation opcode.
|
* @return The name operation opcode.
|
||||||
*/
|
*/
|
||||||
inline opcodetype
|
inline opcodetype getNameOp() const
|
||||||
getNameOp () const
|
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op) {
|
||||||
{
|
|
||||||
case OP_KEVA_PUT:
|
case OP_KEVA_PUT:
|
||||||
return op;
|
return op;
|
||||||
|
|
||||||
|
case OP_KEVA_NAMESPACE:
|
||||||
|
return op;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert (false);
|
assert (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,54 +100,54 @@ public:
|
|||||||
* whether this creates a name index update/entry.
|
* whether this creates a name index update/entry.
|
||||||
* @return True iff this is NAME_FIRSTUPDATE or NAME_UPDATE.
|
* @return True iff this is NAME_FIRSTUPDATE or NAME_UPDATE.
|
||||||
*/
|
*/
|
||||||
inline bool
|
inline bool isAnyUpdate () const
|
||||||
isAnyUpdate () const
|
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op) {
|
||||||
{
|
|
||||||
case OP_KEVA_PUT:
|
case OP_KEVA_PUT:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case OP_KEVA_NAMESPACE:
|
||||||
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert (false);
|
assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name operation name. This call is only valid for
|
* Return the name operation name. This call is only valid for
|
||||||
* OP_NAME_FIRSTUPDATE or OP_NAME_UPDATE.
|
* OP_KEVA_NAMESPACE or OP_KEVA_PUT.
|
||||||
* @return The name operation's name.
|
* @return The name operation's name.
|
||||||
*/
|
*/
|
||||||
inline const valtype&
|
inline const valtype& getOpName () const
|
||||||
getOpName () const
|
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op) {
|
||||||
{
|
|
||||||
case OP_KEVA_PUT:
|
case OP_KEVA_PUT:
|
||||||
return args[0];
|
return args[0];
|
||||||
|
|
||||||
|
case OP_KEVA_NAMESPACE:
|
||||||
|
return args[0];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert (false);
|
assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name operation value. This call is only valid for
|
* Return the name operation value. This call is only valid for
|
||||||
* OP_NAME_FIRSTUPDATE or OP_NAME_UPDATE.
|
* OP_KEVA_PUT.
|
||||||
* @return The name operation's value.
|
* @return The name operation's value.
|
||||||
*/
|
*/
|
||||||
inline const valtype&
|
inline const valtype& getOpValue () const
|
||||||
getOpValue () const
|
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op) {
|
||||||
{
|
|
||||||
case OP_KEVA_PUT:
|
case OP_KEVA_PUT:
|
||||||
// args[1] is namespace
|
// args[1] is namespace
|
||||||
return args[2];
|
return args[2];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert (false);
|
assert (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,8 +155,7 @@ public:
|
|||||||
* for OP_NAME_FIRSTUPDATE.
|
* for OP_NAME_FIRSTUPDATE.
|
||||||
* @return The name operation's rand.
|
* @return The name operation's rand.
|
||||||
*/
|
*/
|
||||||
inline const valtype&
|
inline const valtype& getOpRand() const
|
||||||
getOpRand () const
|
|
||||||
{
|
{
|
||||||
assert (op == OP_NAME_FIRSTUPDATE);
|
assert (op == OP_NAME_FIRSTUPDATE);
|
||||||
return args[1];
|
return args[1];
|
||||||
|
@ -57,9 +57,7 @@ enum opcodetype
|
|||||||
OP_RESERVED = 0x50,
|
OP_RESERVED = 0x50,
|
||||||
OP_1 = 0x51,
|
OP_1 = 0x51,
|
||||||
OP_TRUE=OP_1,
|
OP_TRUE=OP_1,
|
||||||
OP_KEVA_PUT=OP_1,
|
|
||||||
OP_2 = 0x52,
|
OP_2 = 0x52,
|
||||||
OP_KEVA_NAMESPACE=OP_2,
|
|
||||||
OP_3 = 0x53,
|
OP_3 = 0x53,
|
||||||
OP_4 = 0x54,
|
OP_4 = 0x54,
|
||||||
OP_5 = 0x55,
|
OP_5 = 0x55,
|
||||||
@ -183,6 +181,10 @@ enum opcodetype
|
|||||||
OP_NOP9 = 0xb8,
|
OP_NOP9 = 0xb8,
|
||||||
OP_NOP10 = 0xb9,
|
OP_NOP10 = 0xb9,
|
||||||
|
|
||||||
|
// Keva
|
||||||
|
OP_KEVA_PUT=0xd0,
|
||||||
|
OP_KEVA_NAMESPACE=0xd1,
|
||||||
|
|
||||||
|
|
||||||
// template matching params
|
// template matching params
|
||||||
OP_SMALLINTEGER = 0xfa,
|
OP_SMALLINTEGER = 0xfa,
|
||||||
|
@ -50,9 +50,9 @@ keva_namespace (const JSONRPCRequest& request)
|
|||||||
|
|
||||||
ObserveSafeMode ();
|
ObserveSafeMode ();
|
||||||
|
|
||||||
const std::string nameStr = request.params[0].get_str ();
|
const std::string displayNameStr = request.params[0].get_str ();
|
||||||
const valtype name = ValtypeFromString (nameStr);
|
const valtype displayName = ValtypeFromString (displayNameStr);
|
||||||
if (name.size () > MAX_NAME_LENGTH)
|
if (displayName.size () > MAX_NAME_LENGTH)
|
||||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
|
||||||
|
|
||||||
/* No explicit locking should be necessary. CReserveKey takes care
|
/* No explicit locking should be necessary. CReserveKey takes care
|
||||||
@ -65,39 +65,115 @@ keva_namespace (const JSONRPCRequest& request)
|
|||||||
CPubKey pubKey;
|
CPubKey pubKey;
|
||||||
const bool ok = keyName.GetReservedKey (pubKey, true);
|
const bool ok = keyName.GetReservedKey (pubKey, true);
|
||||||
assert (ok);
|
assert (ok);
|
||||||
const CScript addrName = GetScriptForDestination (pubKey.GetID ());
|
const CScript addrName = GetScriptForDestination(pubKey.GetID());
|
||||||
const CScript newScript = CNameScript::buildNameNew (addrName, hash);
|
const CScript newScript = CKevaScript::buildKevaNamespace(addrName, namespaceHash, displayName);
|
||||||
|
|
||||||
valtype rand(20);
|
//const uint160 hash = Hash160(toHash);
|
||||||
GetRandBytes (&rand[0], rand.size ());
|
|
||||||
|
|
||||||
valtype toHash(rand);
|
|
||||||
toHash.insert (toHash.end (), name.begin (), name.end ());
|
|
||||||
const uint160 hash = Hash160 (toHash);
|
|
||||||
|
|
||||||
CCoinControl coinControl;
|
CCoinControl coinControl;
|
||||||
CWalletTx wtx;
|
CWalletTx wtx;
|
||||||
SendMoneyToScript (pwallet, newScript, nullptr,
|
SendMoneyToScript (pwallet, newScript, nullptr,
|
||||||
NAME_LOCKED_AMOUNT, false, wtx, coinControl);
|
NAME_LOCKED_AMOUNT, false, wtx, coinControl);
|
||||||
|
|
||||||
keyName.KeepKey ();
|
keyName.KeepKey();
|
||||||
|
|
||||||
const std::string randStr = HexStr (rand);
|
const std::string randStr = HexStr(rand);
|
||||||
const std::string txid = wtx.GetHash ().GetHex ();
|
const std::string txid = wtx.GetHash().GetHex();
|
||||||
LogPrintf ("name_new: name=%s, rand=%s, tx=%s\n",
|
LogPrintf ("name_new: name=%s, rand=%s, tx=%s\n",
|
||||||
nameStr.c_str (), randStr.c_str (), txid.c_str ());
|
nameStr.c_str(), randStr.c_str(), txid.c_str());
|
||||||
|
|
||||||
UniValue res(UniValue::VARR);
|
UniValue res(UniValue::VARR);
|
||||||
res.push_back (txid);
|
res.push_back(txid);
|
||||||
res.push_back (randStr);
|
res.push_back(randStr);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
UniValue
|
UniValue keva_namespace(const JSONRPCRequest& request)
|
||||||
keva_put (const JSONRPCRequest& request)
|
{
|
||||||
|
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
|
||||||
|
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
|
||||||
|
return NullUniValue;
|
||||||
|
|
||||||
|
if (request.fHelp
|
||||||
|
|| (request.params.size() != 3))
|
||||||
|
throw std::runtime_error (
|
||||||
|
"keva_namespace \"display_name\" (\"keva_namespace\")\n"
|
||||||
|
"\nCreate a namespace.\n"
|
||||||
|
+ HelpRequiringPassphrase(pwallet) +
|
||||||
|
"\nArguments:\n"
|
||||||
|
"1. \"display_name\" (string, required) the display name of the namespace\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"\"txid\" (string) the keva_namespace's txid\n"
|
||||||
|
"\"internal_id\" (string) the internal id of the namespace\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("keva_namespace", "\"display_name\"")
|
||||||
|
);
|
||||||
|
|
||||||
|
RPCTypeCheck (request.params,
|
||||||
|
{UniValue::VSTR});
|
||||||
|
|
||||||
|
ObserveSafeMode ();
|
||||||
|
|
||||||
|
const std::string displayName = request.params[0].get_str();
|
||||||
|
const valtype displayNameVal = ValtypeFromString(displayName);
|
||||||
|
if (displayNameVal.size() > MAX_NAMESPACE_LENGTH)
|
||||||
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the display name of the namespace is too long");
|
||||||
|
|
||||||
|
/* Reject updates to a name for which the mempool already has
|
||||||
|
a pending update. This is not a hard rule enforced by network
|
||||||
|
rules, but it is necessary with the current mempool implementation. */
|
||||||
|
{
|
||||||
|
LOCK (mempool.cs);
|
||||||
|
if (mempool.updatesName(name))
|
||||||
|
throw JSONRPCError (RPC_TRANSACTION_ERROR,
|
||||||
|
"there is already a pending update for this name");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No more locking required, similarly to name_new. */
|
||||||
|
EnsureWalletIsUnlocked (pwallet);
|
||||||
|
|
||||||
|
CReserveKey keyName(pwallet);
|
||||||
|
CPubKey pubKeyReserve;
|
||||||
|
const bool ok = keyName.GetReservedKey(pubKeyReserve, true);
|
||||||
|
assert (ok);
|
||||||
|
bool usedKey = false;
|
||||||
|
|
||||||
|
CScript addrName;
|
||||||
|
if (request.params.size () == 3)
|
||||||
|
{
|
||||||
|
keyName.ReturnKey ();
|
||||||
|
const CTxDestination dest
|
||||||
|
= DecodeDestination (request.params[2].get_str ());
|
||||||
|
if (!IsValidDestination (dest))
|
||||||
|
throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, "invalid address");
|
||||||
|
|
||||||
|
addrName = GetScriptForDestination (dest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usedKey = true;
|
||||||
|
addrName = GetScriptForDestination (pubKeyReserve.GetID ());
|
||||||
|
}
|
||||||
|
|
||||||
|
const CScript nameScript
|
||||||
|
= CNameScript::buildNameUpdate (addrName, name, value);
|
||||||
|
|
||||||
|
CCoinControl coinControl;
|
||||||
|
CWalletTx wtx;
|
||||||
|
SendMoneyToScript (pwallet, nameScript, &txIn,
|
||||||
|
NAME_LOCKED_AMOUNT, false, wtx, coinControl);
|
||||||
|
|
||||||
|
if (usedKey)
|
||||||
|
keyName.KeepKey ();
|
||||||
|
|
||||||
|
return wtx.GetHash ().GetHex ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UniValue keva_put(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
|
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
|
||||||
if (!EnsureWalletIsAvailable (pwallet, request.fHelp))
|
if (!EnsureWalletIsAvailable (pwallet, request.fHelp))
|
||||||
|
@ -440,6 +440,56 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendMoneyToScript(CWallet* const pwallet, const CScript &scriptPubKey,
|
||||||
|
const CTxIn* withInput, CAmount nValue,
|
||||||
|
bool fSubtractFeeFromAmount, CWalletTx& wtxNew,
|
||||||
|
const CCoinControl& coin_control)
|
||||||
|
{
|
||||||
|
CAmount curBalance = pwallet->GetBalance();
|
||||||
|
|
||||||
|
// Check amount
|
||||||
|
if (nValue <= 0)
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
|
||||||
|
|
||||||
|
/* If we have an additional input that is a name, we have to take this
|
||||||
|
name's value into account as well for the balance check. Otherwise one
|
||||||
|
sees spurious "Insufficient funds" errors when updating names when the
|
||||||
|
wallet's balance it smaller than the amount locked in the name. */
|
||||||
|
CAmount lockedValue = 0;
|
||||||
|
std::string strError;
|
||||||
|
if (withInput) {
|
||||||
|
const CWalletTx* dummyWalletTx;
|
||||||
|
if (!pwallet->FindValueInNameInput (*withInput, lockedValue,
|
||||||
|
dummyWalletTx, strError))
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nValue > curBalance + lockedValue)
|
||||||
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
|
||||||
|
|
||||||
|
if (pwallet->GetBroadcastTransactions() && !g_connman) {
|
||||||
|
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and send the transaction
|
||||||
|
CReserveKey reservekey(pwallet);
|
||||||
|
CAmount nFeeRequired;
|
||||||
|
std::vector<CRecipient> vecSend;
|
||||||
|
int nChangePosRet = -1;
|
||||||
|
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
|
||||||
|
vecSend.push_back(recipient);
|
||||||
|
if (!pwallet->CreateTransaction(vecSend, withInput, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
|
||||||
|
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
|
||||||
|
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
|
}
|
||||||
|
CValidationState state;
|
||||||
|
if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) {
|
||||||
|
strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason());
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UniValue sendtoaddress(const JSONRPCRequest& request)
|
UniValue sendtoaddress(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
|
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
|
||||||
@ -3532,11 +3582,8 @@ extern UniValue removeprunedfunds(const JSONRPCRequest& request);
|
|||||||
extern UniValue importmulti(const JSONRPCRequest& request);
|
extern UniValue importmulti(const JSONRPCRequest& request);
|
||||||
extern UniValue rescanblockchain(const JSONRPCRequest& request);
|
extern UniValue rescanblockchain(const JSONRPCRequest& request);
|
||||||
|
|
||||||
//extern UniValue keva_put(const JSONRPCRequest& request); // in rpckeva.cpp
|
extern UniValue keva_namespace(const JSONRPCRequest& request); // in rpckeva.cpp
|
||||||
UniValue keva_put(const JSONRPCRequest& request) {
|
extern UniValue keva_put(const JSONRPCRequest& request); // in rpckeva.cpp
|
||||||
UniValue ret();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const CRPCCommand commands[] =
|
static const CRPCCommand commands[] =
|
||||||
{ // category name actor (function) argNames
|
{ // category name actor (function) argNames
|
||||||
@ -3596,7 +3643,8 @@ static const CRPCCommand commands[] =
|
|||||||
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
|
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
|
||||||
|
|
||||||
// Kevacoin-specific wallet calls.
|
// Kevacoin-specific wallet calls.
|
||||||
{ "kevacoin", "keva_put", &keva_put, {"namespace", "key", "value", "create_namespace"} }
|
{ "kevacoin", "keva_namespace", &keva_namespace, {"namespace", "key", "value", "create_namespace"} }
|
||||||
|
{ "kevacoin", "keva_put", &keva_put, {"namespace", "key", "value", "put_value"} }
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterWalletRPCCommands(CRPCTable &t)
|
void RegisterWalletRPCCommands(CRPCTable &t)
|
||||||
|
@ -24,5 +24,9 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
|
|||||||
std::string HelpRequiringPassphrase(CWallet *);
|
std::string HelpRequiringPassphrase(CWallet *);
|
||||||
void EnsureWalletIsUnlocked(CWallet *);
|
void EnsureWalletIsUnlocked(CWallet *);
|
||||||
bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
|
bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
|
||||||
|
void SendMoneyToScript(CWallet* pwallet, const CScript& scriptPubKey,
|
||||||
|
const CTxIn* withInput, CAmount nValue,
|
||||||
|
bool fSubtractFeeFromAmount, CWalletTx& wtxNew,
|
||||||
|
const CCoinControl& coin_control);
|
||||||
|
|
||||||
#endif //BITCOIN_WALLET_RPCWALLET_H
|
#endif //BITCOIN_WALLET_RPCWALLET_H
|
||||||
|
@ -2662,7 +2662,9 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
|
|||||||
return g_address_type;
|
return g_address_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
|
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend,
|
||||||
|
const CTxIn* withInput,
|
||||||
|
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
|
||||||
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
|
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
|
||||||
{
|
{
|
||||||
CAmount nValue = 0;
|
CAmount nValue = 0;
|
||||||
@ -2686,10 +2688,26 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we have an input to include, find its value. This value will be
|
||||||
|
subtracted later on during coin selection, since the input is added
|
||||||
|
additionally to the selected coins. */
|
||||||
|
CAmount nInputValue = 0;
|
||||||
|
const CWalletTx* withInputTx = nullptr;
|
||||||
|
if (withInput) {
|
||||||
|
if (!FindValueInNameInput(*withInput, nInputValue, withInputTx, strFailReason))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
wtxNew.fTimeReceivedIsTxTime = true;
|
wtxNew.fTimeReceivedIsTxTime = true;
|
||||||
wtxNew.BindWallet(this);
|
wtxNew.BindWallet(this);
|
||||||
CMutableTransaction txNew;
|
CMutableTransaction txNew;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// JWU TODO implement this!
|
||||||
|
if (isNamecoin)
|
||||||
|
txNew.SetNamecoin();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Discourage fee sniping.
|
// Discourage fee sniping.
|
||||||
//
|
//
|
||||||
// For a large miner the value of the transactions in the best block and
|
// For a large miner the value of the transactions in the best block and
|
||||||
@ -3023,6 +3041,37 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CWallet::FindValueInNameInput (const CTxIn& nameInput,
|
||||||
|
CAmount& value, const CWalletTx*& walletTx,
|
||||||
|
std::string& strFailReason) const
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
// JWU TODO: implement this!
|
||||||
|
walletTx = GetWalletTx (nameInput.prevout.hash);
|
||||||
|
if (!walletTx) {
|
||||||
|
strFailReason = _("Input tx not found in wallet");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CTxOut& output = walletTx->tx->vout[nameInput.prevout.n];
|
||||||
|
if (IsMine (output) != ISMINE_SPENDABLE) {
|
||||||
|
strFailReason = _("Input tx is not mine");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CKevaScript::isKevaScript (output.scriptPubKey)) {
|
||||||
|
strFailReason = _("Input tx is not a name operation");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = output.nValue;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call after CreateTransaction unless you want to abort
|
* Call after CreateTransaction unless you want to abort
|
||||||
*/
|
*/
|
||||||
|
@ -974,12 +974,22 @@ public:
|
|||||||
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
|
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
|
||||||
bool SignTransaction(CMutableTransaction& tx);
|
bool SignTransaction(CMutableTransaction& tx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the amount in the given tx input. This must only be called with
|
||||||
|
* Namecoin inputs as used for CreateTransaction.
|
||||||
|
*/
|
||||||
|
bool FindValueInNameInput (const CTxIn& nameInput,
|
||||||
|
CAmount& value, const CWalletTx*& walletTx,
|
||||||
|
std::string& strFailReason) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new transaction paying the recipients with a set of coins
|
* Create a new transaction paying the recipients with a set of coins
|
||||||
* selected by SelectCoins(); Also create the change output, when needed
|
* selected by SelectCoins(); Also create the change output, when needed
|
||||||
* @note passing nChangePosInOut as -1 will result in setting a random position
|
* @note passing nChangePosInOut as -1 will result in setting a random position
|
||||||
*/
|
*/
|
||||||
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
|
bool CreateTransaction(const std::vector<CRecipient>& vecSend,
|
||||||
|
const CTxIn* withInput,
|
||||||
|
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
|
||||||
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
|
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
|
||||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
|
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user