mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-02-05 03:34:24 +00:00
WIP: started implementing keva_namespace.
This commit is contained in:
parent
7b6206d83a
commit
fb0acfc6d9
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
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
@ -19,29 +23,29 @@
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* CNameTxUndo. */
|
||||
/* CKevaTxUndo. */
|
||||
|
||||
void
|
||||
CNameTxUndo::fromOldState (const valtype& nm, const CCoinsView& view)
|
||||
CKevaTxUndo::fromOldState(const valtype& nm, const CCoinsView& view)
|
||||
{
|
||||
name = nm;
|
||||
isNew = !view.GetName (name, oldData);
|
||||
isNew = !view.GetName(name, oldData);
|
||||
}
|
||||
|
||||
void
|
||||
CNameTxUndo::apply (CCoinsViewCache& view) const
|
||||
CKevaTxUndo::apply (CCoinsViewCache& view) const
|
||||
{
|
||||
if (isNew)
|
||||
view.DeleteName (name);
|
||||
view.DeleteName(name);
|
||||
else
|
||||
view.SetName (name, oldData, true);
|
||||
view.SetName(name, oldData, true);
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* CKevaMemPool. */
|
||||
|
||||
uint256
|
||||
CKevaMemPool::getTxForName (const valtype& name) const
|
||||
CKevaMemPool::getTxForName(const valtype& name) const
|
||||
{
|
||||
NameTxMap::const_iterator mi;
|
||||
|
||||
@ -536,7 +540,7 @@ ApplyNameTransaction (const CTransaction& tx, unsigned nHeight,
|
||||
LogPrint (BCLog::NAMES, "Updating name at height %d: %s\n",
|
||||
nHeight, ValtypeToString (name).c_str ());
|
||||
|
||||
CNameTxUndo opUndo;
|
||||
CKevaTxUndo opUndo;
|
||||
opUndo.fromOldState (name, view);
|
||||
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
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// 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;
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* CNameTxUndo. */
|
||||
/* CKevaTxUndo. */
|
||||
|
||||
/**
|
||||
* Undo information for one name operation. This contains either the
|
||||
* information that the name was newly created (and should thus be
|
||||
* deleted entirely) or that it was updated including the old value.
|
||||
*/
|
||||
class CNameTxUndo
|
||||
class CKevaTxUndo
|
||||
{
|
||||
|
||||
private:
|
||||
|
@ -441,11 +441,14 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
if(!g_connman)
|
||||
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)
|
||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Litecoin is not connected!");
|
||||
|
||||
if (IsInitialBlockDownload())
|
||||
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Litecoin is downloading blocks...");
|
||||
*/
|
||||
|
||||
static unsigned int nTransactionsUpdatedLast;
|
||||
|
||||
|
@ -15,39 +15,48 @@ CKevaScript::CKevaScript (const CScript& script)
|
||||
return;
|
||||
|
||||
opcodetype opcode;
|
||||
while (true)
|
||||
{
|
||||
valtype vch;
|
||||
while (true) {
|
||||
valtype vch;
|
||||
|
||||
if (!script.GetOp (pc, opcode, vch))
|
||||
return;
|
||||
if (opcode == OP_DROP || opcode == OP_2DROP || opcode == OP_NOP)
|
||||
break;
|
||||
if (!(opcode >= 0 && opcode <= OP_PUSHDATA4))
|
||||
return;
|
||||
if (!script.GetOp (pc, opcode, vch)) {
|
||||
return;
|
||||
}
|
||||
if (opcode == OP_DROP || opcode == OP_2DROP || opcode == OP_NOP) {
|
||||
break;
|
||||
}
|
||||
if (!(opcode >= 0 && opcode <= OP_PUSHDATA4)) {
|
||||
return;
|
||||
}
|
||||
|
||||
args.push_back (vch);
|
||||
}
|
||||
args.push_back (vch);
|
||||
}
|
||||
|
||||
// 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))
|
||||
break;
|
||||
break;
|
||||
}
|
||||
pc--;
|
||||
|
||||
/* 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
|
||||
op and address members, if everything is valid. */
|
||||
switch (nameOp)
|
||||
{
|
||||
switch (nameOp) {
|
||||
case OP_KEVA_PUT:
|
||||
if (args.size () != 1)
|
||||
if (args.size () != 1) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_KEVA_NAMESPACE:
|
||||
if (args.size () != 1) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
op = nameOp;
|
||||
address = CScript (pc, script.end ());
|
||||
@ -58,8 +67,7 @@ CKevaScript::buildKevaPut(const CScript& addr, const valtype& nameSpace,
|
||||
const valtype& key, const valtype& value)
|
||||
{
|
||||
CScript prefix;
|
||||
prefix << OP_KEVA_PUT << nameSpace << key << value
|
||||
<< OP_2DROP << OP_2DROP;
|
||||
prefix << OP_KEVA_PUT << nameSpace << key << value << OP_2DROP << OP_DROP;
|
||||
|
||||
return prefix + addr;
|
||||
}
|
||||
@ -68,8 +76,7 @@ CScript CKevaScript::buildKevaNamespace(const CScript& addr, const valtype& name
|
||||
const valtype& displayName)
|
||||
{
|
||||
CScript prefix;
|
||||
prefix << OP_KEVA_NAMESPACE << nameSpace << displayName
|
||||
<< OP_2DROP << OP_2DROP;
|
||||
prefix << OP_KEVA_NAMESPACE << nameSpace << displayName << OP_2DROP;
|
||||
|
||||
return prefix + addr;
|
||||
}
|
||||
|
@ -52,25 +52,26 @@ public:
|
||||
inline bool
|
||||
isNameOp () const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case OP_KEVA_PUT:
|
||||
switch (op) {
|
||||
case OP_KEVA_PUT:
|
||||
return true;
|
||||
|
||||
case OP_KEVA_NAMESPACE:
|
||||
return true;
|
||||
|
||||
case OP_NOP:
|
||||
return false;
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the non-name script.
|
||||
* @return The address part.
|
||||
*/
|
||||
inline const CScript&
|
||||
getAddress () const
|
||||
inline const CScript& getAddress() const
|
||||
{
|
||||
return address;
|
||||
}
|
||||
@ -80,17 +81,18 @@ public:
|
||||
* or OP_NAME_UPDATE. Do not call if this is not a name script.
|
||||
* @return The name operation opcode.
|
||||
*/
|
||||
inline opcodetype
|
||||
getNameOp () const
|
||||
inline opcodetype getNameOp() const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
switch (op) {
|
||||
case OP_KEVA_PUT:
|
||||
return op;
|
||||
|
||||
case OP_KEVA_NAMESPACE:
|
||||
return op;
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,54 +100,54 @@ public:
|
||||
* whether this creates a name index update/entry.
|
||||
* @return True iff this is NAME_FIRSTUPDATE or NAME_UPDATE.
|
||||
*/
|
||||
inline bool
|
||||
isAnyUpdate () const
|
||||
inline bool isAnyUpdate () const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
switch (op) {
|
||||
case OP_KEVA_PUT:
|
||||
return true;
|
||||
return true;
|
||||
|
||||
case OP_KEVA_NAMESPACE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
inline const valtype&
|
||||
getOpName () const
|
||||
inline const valtype& getOpName () const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
switch (op) {
|
||||
case OP_KEVA_PUT:
|
||||
return args[0];
|
||||
|
||||
case OP_KEVA_NAMESPACE:
|
||||
return args[0];
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
inline const valtype&
|
||||
getOpValue () const
|
||||
inline const valtype& getOpValue () const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
switch (op) {
|
||||
case OP_KEVA_PUT:
|
||||
// args[1] is namespace
|
||||
return args[2];
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,8 +155,7 @@ public:
|
||||
* for OP_NAME_FIRSTUPDATE.
|
||||
* @return The name operation's rand.
|
||||
*/
|
||||
inline const valtype&
|
||||
getOpRand () const
|
||||
inline const valtype& getOpRand() const
|
||||
{
|
||||
assert (op == OP_NAME_FIRSTUPDATE);
|
||||
return args[1];
|
||||
|
@ -57,9 +57,7 @@ enum opcodetype
|
||||
OP_RESERVED = 0x50,
|
||||
OP_1 = 0x51,
|
||||
OP_TRUE=OP_1,
|
||||
OP_KEVA_PUT=OP_1,
|
||||
OP_2 = 0x52,
|
||||
OP_KEVA_NAMESPACE=OP_2,
|
||||
OP_3 = 0x53,
|
||||
OP_4 = 0x54,
|
||||
OP_5 = 0x55,
|
||||
@ -183,6 +181,10 @@ enum opcodetype
|
||||
OP_NOP9 = 0xb8,
|
||||
OP_NOP10 = 0xb9,
|
||||
|
||||
// Keva
|
||||
OP_KEVA_PUT=0xd0,
|
||||
OP_KEVA_NAMESPACE=0xd1,
|
||||
|
||||
|
||||
// template matching params
|
||||
OP_SMALLINTEGER = 0xfa,
|
||||
|
@ -50,9 +50,9 @@ keva_namespace (const JSONRPCRequest& request)
|
||||
|
||||
ObserveSafeMode ();
|
||||
|
||||
const std::string nameStr = request.params[0].get_str ();
|
||||
const valtype name = ValtypeFromString (nameStr);
|
||||
if (name.size () > MAX_NAME_LENGTH)
|
||||
const std::string displayNameStr = request.params[0].get_str ();
|
||||
const valtype displayName = ValtypeFromString (displayNameStr);
|
||||
if (displayName.size () > MAX_NAME_LENGTH)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
|
||||
|
||||
/* No explicit locking should be necessary. CReserveKey takes care
|
||||
@ -65,39 +65,115 @@ keva_namespace (const JSONRPCRequest& request)
|
||||
CPubKey pubKey;
|
||||
const bool ok = keyName.GetReservedKey (pubKey, true);
|
||||
assert (ok);
|
||||
const CScript addrName = GetScriptForDestination (pubKey.GetID ());
|
||||
const CScript newScript = CNameScript::buildNameNew (addrName, hash);
|
||||
const CScript addrName = GetScriptForDestination(pubKey.GetID());
|
||||
const CScript newScript = CKevaScript::buildKevaNamespace(addrName, namespaceHash, displayName);
|
||||
|
||||
valtype rand(20);
|
||||
GetRandBytes (&rand[0], rand.size ());
|
||||
|
||||
valtype toHash(rand);
|
||||
toHash.insert (toHash.end (), name.begin (), name.end ());
|
||||
const uint160 hash = Hash160 (toHash);
|
||||
//const uint160 hash = Hash160(toHash);
|
||||
|
||||
CCoinControl coinControl;
|
||||
CWalletTx wtx;
|
||||
SendMoneyToScript (pwallet, newScript, nullptr,
|
||||
NAME_LOCKED_AMOUNT, false, wtx, coinControl);
|
||||
|
||||
keyName.KeepKey ();
|
||||
keyName.KeepKey();
|
||||
|
||||
const std::string randStr = HexStr (rand);
|
||||
const std::string txid = wtx.GetHash ().GetHex ();
|
||||
const std::string randStr = HexStr(rand);
|
||||
const std::string txid = wtx.GetHash().GetHex();
|
||||
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);
|
||||
res.push_back (txid);
|
||||
res.push_back (randStr);
|
||||
res.push_back(txid);
|
||||
res.push_back(randStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
UniValue
|
||||
keva_put (const JSONRPCRequest& request)
|
||||
UniValue keva_namespace(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);
|
||||
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)
|
||||
{
|
||||
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
@ -3532,11 +3582,8 @@ extern UniValue removeprunedfunds(const JSONRPCRequest& request);
|
||||
extern UniValue importmulti(const JSONRPCRequest& request);
|
||||
extern UniValue rescanblockchain(const JSONRPCRequest& request);
|
||||
|
||||
//extern UniValue keva_put(const JSONRPCRequest& request); // in rpckeva.cpp
|
||||
UniValue keva_put(const JSONRPCRequest& request) {
|
||||
UniValue ret();
|
||||
return ret;
|
||||
}
|
||||
extern UniValue keva_namespace(const JSONRPCRequest& request); // in rpckeva.cpp
|
||||
extern UniValue keva_put(const JSONRPCRequest& request); // in rpckeva.cpp
|
||||
|
||||
static const CRPCCommand commands[] =
|
||||
{ // category name actor (function) argNames
|
||||
@ -3596,7 +3643,8 @@ static const CRPCCommand commands[] =
|
||||
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
|
||||
|
||||
// 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)
|
||||
|
@ -24,5 +24,9 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
|
||||
std::string HelpRequiringPassphrase(CWallet *);
|
||||
void EnsureWalletIsUnlocked(CWallet *);
|
||||
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
|
||||
|
@ -2662,7 +2662,9 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
|
||||
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)
|
||||
{
|
||||
CAmount nValue = 0;
|
||||
@ -2686,10 +2688,26 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
||||
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.BindWallet(this);
|
||||
CMutableTransaction txNew;
|
||||
|
||||
#if 0
|
||||
// JWU TODO implement this!
|
||||
if (isNamecoin)
|
||||
txNew.SetNamecoin();
|
||||
#endif
|
||||
|
||||
// Discourage fee sniping.
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -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 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
|
||||
* selected by SelectCoins(); Also create the change output, when needed
|
||||
* @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);
|
||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user