Browse Source

WIP: started implementing keva_namespace.

cn
Jianping Wu 6 years ago
parent
commit
fb0acfc6d9
  1. 295
      src/keva/common.cpp
  2. 485
      src/keva/common.h
  3. 20
      src/keva/main.cpp
  4. 8
      src/keva/main.h
  5. 3
      src/rpc/mining.cpp
  6. 49
      src/script/keva.cpp
  7. 69
      src/script/keva.h
  8. 6
      src/script/script.h
  9. 114
      src/wallet/rpckeva.cpp
  10. 60
      src/wallet/rpcwallet.cpp
  11. 4
      src/wallet/rpcwallet.h
  12. 51
      src/wallet/wallet.cpp
  13. 12
      src/wallet/wallet.h

295
src/keva/common.cpp

@ -0,0 +1,295 @@ @@ -0,0 +1,295 @@
// Copyright (c) 2014-2017 Daniel Kraft
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// Copyright (c) 2018 Jianping Wu
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <keva/common.h>
#include <script/keva.h>
bool fNameHistory = false;
/* ************************************************************************** */
/* CKevaData. */
void
CKevaData::fromScript (unsigned h, const COutPoint& out,
const CKevaScript& script)
{
assert (script.isAnyUpdate ());
value = script.getOpValue ();
nHeight = h;
prevout = out;
addr = script.getAddress ();
}
/* ************************************************************************** */
/* CNameIterator. */
CNameIterator::~CNameIterator ()
{
/* Nothing to be done here. This may be overwritten by
subclasses if they need a destructor. */
}
/* ************************************************************************** */
/* CNameCacheNameIterator. */
class CCacheNameIterator : public CNameIterator
{
private:
/** Reference to cache object that is used. */
const CNameCache& cache;
/** Base iterator to combine with the cache. */
CNameIterator* base;
/** Whether or not the base iterator has more entries. */
bool baseHasMore;
/** "Next" name of the base iterator. */
valtype baseName;
/** "Next" data of the base iterator. */
CKevaData baseData;
/** Iterator of the cache's entries. */
CNameCache::EntryMap::const_iterator cacheIter;
/* Call the base iterator's next() routine to fill in the internal
"cache" for the next entry. This already skips entries that are
marked as deleted in the cache. */
void advanceBaseIterator ();
public:
/**
* Construct the iterator. This takes ownership of the base iterator.
* @param c The cache object to use.
* @param b The base iterator.
*/
CCacheNameIterator (const CNameCache& c, CNameIterator* b);
/* Destruct, this deletes also the base iterator. */
~CCacheNameIterator ();
/* Implement iterator methods. */
void seek (const valtype& name);
bool next (valtype& name, CKevaData& data);
};
CCacheNameIterator::CCacheNameIterator (const CNameCache& c, CNameIterator* b)
: cache(c), base(b)
{
/* Add a seek-to-start to ensure that everything is consistent. This call
may be superfluous if we seek to another position afterwards anyway,
but it should also not hurt too much. */
seek (valtype ());
}
CCacheNameIterator::~CCacheNameIterator ()
{
delete base;
}
void
CCacheNameIterator::advanceBaseIterator ()
{
assert (baseHasMore);
do
baseHasMore = base->next (baseName, baseData);
while (baseHasMore && cache.isDeleted (baseName));
}
void
CCacheNameIterator::seek (const valtype& start)
{
cacheIter = cache.entries.lower_bound (start);
base->seek (start);
baseHasMore = true;
advanceBaseIterator ();
}
bool
CCacheNameIterator::next (valtype& name, CKevaData& data)
{
/* Exit early if no more data is available in either the cache
nor the base iterator. */
if (!baseHasMore && cacheIter == cache.entries.end ())
return false;
/* Determine which source to use for the next. */
bool useBase;
if (!baseHasMore)
useBase = false;
else if (cacheIter == cache.entries.end ())
useBase = true;
else
{
/* A special case is when both iterators are equal. In this case,
we want to use the cached version. We also have to advance
the base iterator. */
if (baseName == cacheIter->first)
advanceBaseIterator ();
/* Due to advancing the base iterator above, it may happen that
no more base entries are present. Handle this gracefully. */
if (!baseHasMore)
useBase = false;
else
{
assert (baseName != cacheIter->first);
CNameCache::NameComparator cmp;
useBase = cmp (baseName, cacheIter->first);
}
}
/* Use the correct source now and advance it. */
if (useBase)
{
name = baseName;
data = baseData;
advanceBaseIterator ();
}
else
{
name = cacheIter->first;
data = cacheIter->second;
++cacheIter;
}
return true;
}
/* ************************************************************************** */
/* CNameCache. */
bool
CNameCache::get (const valtype& name, CKevaData& data) const
{
const EntryMap::const_iterator i = entries.find (name);
if (i == entries.end ())
return false;
data = i->second;
return true;
}
void
CNameCache::set (const valtype& name, const CKevaData& data)
{
const std::set<valtype>::iterator di = deleted.find (name);
if (di != deleted.end ())
deleted.erase (di);
const EntryMap::iterator ei = entries.find (name);
if (ei != entries.end ())
ei->second = data;
else
entries.insert (std::make_pair (name, data));
}
void
CNameCache::remove (const valtype& name)
{
const EntryMap::iterator ei = entries.find (name);
if (ei != entries.end ())
entries.erase (ei);
deleted.insert (name);
}
CNameIterator*
CNameCache::iterateNames (CNameIterator* base) const
{
return new CCacheNameIterator (*this, base);
}
bool
CNameCache::getHistory (const valtype& name, CNameHistory& res) const
{
assert (fNameHistory);
const std::map<valtype, CNameHistory>::const_iterator i = history.find (name);
if (i == history.end ())
return false;
res = i->second;
return true;
}
void
CNameCache::setHistory (const valtype& name, const CNameHistory& data)
{
assert (fNameHistory);
const std::map<valtype, CNameHistory>::iterator ei = history.find (name);
if (ei != history.end ())
ei->second = data;
else
history.insert (std::make_pair (name, data));
}
void
CNameCache::updateNamesForHeight (unsigned nHeight,
std::set<valtype>& names) const
{
/* Seek in the map of cached entries to the first one corresponding
to our height. */
const ExpireEntry seekEntry(nHeight, valtype ());
std::map<ExpireEntry, bool>::const_iterator it;
for (it = expireIndex.lower_bound (seekEntry); it != expireIndex.end (); ++it)
{
const ExpireEntry& cur = it->first;
assert (cur.nHeight >= nHeight);
if (cur.nHeight > nHeight)
break;
if (it->second)
names.insert (cur.name);
else
names.erase (cur.name);
}
}
void
CNameCache::addExpireIndex (const valtype& name, unsigned height)
{
const ExpireEntry entry(height, name);
expireIndex[entry] = true;
}
void
CNameCache::removeExpireIndex (const valtype& name, unsigned height)
{
const ExpireEntry entry(height, name);
expireIndex[entry] = false;
}
void
CNameCache::apply (const CNameCache& cache)
{
for (EntryMap::const_iterator i = cache.entries.begin ();
i != cache.entries.end (); ++i)
set (i->first, i->second);
for (std::set<valtype>::const_iterator i = cache.deleted.begin ();
i != cache.deleted.end (); ++i)
remove (*i);
for (std::map<valtype, CNameHistory>::const_iterator i
= cache.history.begin (); i != cache.history.end (); ++i)
setHistory (i->first, i->second);
for (std::map<ExpireEntry, bool>::const_iterator i
= cache.expireIndex.begin (); i != cache.expireIndex.end (); ++i)
expireIndex[i->first] = i->second;
}

485
src/keva/common.h

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

20
src/keva/main.cpp

@ -1,3 +1,7 @@ @@ -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 @@ @@ -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, @@ -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);

8
src/keva/main.h

@ -1,3 +1,7 @@ @@ -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; @@ -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:

3
src/rpc/mining.cpp

@ -441,11 +441,14 @@ UniValue getblocktemplate(const JSONRPCRequest& request) @@ -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;

49
src/script/keva.cpp

@ -15,39 +15,48 @@ CKevaScript::CKevaScript (const CScript& script) @@ -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, @@ -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 @@ -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;
}

69
src/script/keva.h

@ -52,25 +52,26 @@ public: @@ -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: @@ -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: @@ -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: @@ -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];

6
src/script/script.h

@ -57,9 +57,7 @@ enum opcodetype @@ -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 @@ -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,

114
src/wallet/rpckeva.cpp

@ -50,9 +50,9 @@ keva_namespace (const JSONRPCRequest& request) @@ -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) @@ -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))

60
src/wallet/rpcwallet.cpp

@ -440,6 +440,56 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA @@ -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); @@ -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[] = @@ -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)

4
src/wallet/rpcwallet.h

@ -24,5 +24,9 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request); @@ -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

51
src/wallet/wallet.cpp

@ -2662,7 +2662,9 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec @@ -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 @@ -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 @@ -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
*/

12
src/wallet/wallet.h

@ -974,12 +974,22 @@ public: @@ -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…
Cancel
Save