Browse Source

WIP: support two OPs.

cn
Jianping Wu 6 years ago
parent
commit
855dc114e9
  1. 57
      src/keva/main.cpp
  2. 15
      src/keva/main.h
  3. 75
      src/script/keva.cpp
  4. 207
      src/script/keva.h
  5. 2
      src/script/script.h
  6. 80
      src/wallet/rpckeva.cpp

57
src/keva/main.cpp

@ -18,21 +18,6 @@
#include <validation.h> #include <validation.h>
/* ************************************************************************** */
/* CNameData. */
bool
CNameData::isExpired () const
{
return isExpired (chainActive.Height ());
}
bool
CNameData::isExpired (unsigned h) const
{
return ::isExpired (nHeight, h);
}
/* ************************************************************************** */ /* ************************************************************************** */
/* CNameTxUndo. */ /* CNameTxUndo. */
@ -152,48 +137,6 @@ CKevaMemPool::removeConflicts (const CTransaction& tx)
} }
} }
void
CKevaMemPool::removeUnexpireConflicts (const std::set<valtype>& unexpired)
{
AssertLockHeld (pool.cs);
for (const auto& name : unexpired)
{
LogPrint (BCLog::NAMES, "unexpired: %s, mempool: %u\n",
ValtypeToString (name).c_str (), mapNameRegs.count (name));
const NameTxMap::const_iterator mit = mapNameRegs.find (name);
if (mit != mapNameRegs.end ())
{
const CTxMemPool::txiter mit2 = pool.mapTx.find (mit->second);
assert (mit2 != pool.mapTx.end ());
pool.removeRecursive (mit2->GetTx (),
MemPoolRemovalReason::NAME_CONFLICT);
}
}
}
void
CKevaMemPool::removeExpireConflicts (const std::set<valtype>& expired)
{
AssertLockHeld (pool.cs);
for (const auto& name : expired)
{
LogPrint (BCLog::NAMES, "expired: %s, mempool: %u\n",
ValtypeToString (name).c_str (), mapNameUpdates.count (name));
const NameTxMap::const_iterator mit = mapNameUpdates.find (name);
if (mit != mapNameUpdates.end ())
{
const CTxMemPool::txiter mit2 = pool.mapTx.find (mit->second);
assert (mit2 != pool.mapTx.end ());
pool.removeRecursive (mit2->GetTx (),
MemPoolRemovalReason::NAME_CONFLICT);
}
}
}
void void
CKevaMemPool::check (const CCoinsView& coins) const CKevaMemPool::check (const CCoinsView& coins) const
{ {

15
src/keva/main.h

@ -201,21 +201,6 @@ public:
*/ */
void removeConflicts (const CTransaction& tx); void removeConflicts (const CTransaction& tx);
/**
* Remove conflicts in the mempool due to unexpired names. This removes
* conflicting name registrations that are no longer possible.
* @param unexpired The set of unexpired names.
* @param removed Put removed tx here.
*/
void removeUnexpireConflicts (const std::set<valtype>& unexpired);
/**
* Remove conflicts in the mempool due to expired names. This removes
* conflicting name updates that are no longer possible.
* @param expired The set of expired names.
* @param removed Put removed tx here.
*/
void removeExpireConflicts (const std::set<valtype>& expired);
/** /**
* Perform sanity checks. Throws if it fails. * Perform sanity checks. Throws if it fails.
* @param coins The coins view this represents. * @param coins The coins view this represents.

75
src/script/keva.cpp

@ -0,0 +1,75 @@
// 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.
#include <script/keva.h>
#include <uint256.h>
CKevaScript::CKevaScript (const CScript& script)
: op(OP_NOP), address(script)
{
opcodetype nameOp;
CScript::const_iterator pc = script.begin ();
if (!script.GetOp (pc, nameOp))
return;
opcodetype opcode;
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;
args.push_back (vch);
}
// Move the pc to after any DROP or NOP.
while (opcode == OP_DROP || opcode == OP_2DROP || opcode == OP_NOP)
if (!script.GetOp (pc, opcode))
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)
{
case OP_KEVA_PUT:
if (args.size () != 1)
return;
break;
default:
return;
}
op = nameOp;
address = CScript (pc, script.end ());
}
CScript
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;
return prefix + addr;
}
CScript CKevaScript::buildKevaNamespace(const CScript& addr, const valtype& nameSpace,
const valtype& displayName) {
CScript prefix;
prefix << OP_KEVA_NAMESPACE << nameSpace << displayName
<< OP_2DROP << OP_2DROP;
return prefix + addr;
}

207
src/script/keva.h

@ -0,0 +1,207 @@
// 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_SCRIPT_KEVA
#define H_BITCOIN_SCRIPT_KEVA
#include <script/script.h>
class uint160;
/**
* A script parsed for keva operations. This can be initialised
* from a "standard" script, and will then determine if this is
* a name operation and which parts it consists of.
*/
class CKevaScript
{
private:
/** The type of operation. OP_NOP if no (valid) name op. */
opcodetype op;
/** The non-name part, i. e., the address. */
CScript address;
/** The operation arguments. */
std::vector<valtype> args;
public:
/**
* Default constructor. This enables us to declare a variable
* and initialise it later via assignment.
*/
inline CKevaScript ()
: op(OP_NOP)
{}
/**
* Parse a script and determine whether it is a valid name script. Sets
* the member variables representing the "picked apart" name script.
* @param script The ordinary script to parse.
*/
explicit CKevaScript (const CScript& script);
/**
* Return whether this is a (valid) name script.
* @return True iff this is a name operation.
*/
inline bool
isNameOp () const
{
switch (op)
{
case OP_KEVA_PUT:
return true;
case OP_NOP:
return false;
default:
assert (false);
}
}
/**
* Return the non-name script.
* @return The address part.
*/
inline const CScript&
getAddress () const
{
return address;
}
/**
* Return the name operation. This returns OP_NAME_NEW, OP_NAME_FIRSTUPDATE
* or OP_NAME_UPDATE. Do not call if this is not a name script.
* @return The name operation opcode.
*/
inline opcodetype
getNameOp () const
{
switch (op)
{
case OP_KEVA_PUT:
return op;
default:
assert (false);
}
}
/**
* Return whether this is a name update (including first updates). I. e.,
* whether this creates a name index update/entry.
* @return True iff this is NAME_FIRSTUPDATE or NAME_UPDATE.
*/
inline bool
isAnyUpdate () const
{
switch (op)
{
case OP_KEVA_PUT:
return true;
default:
assert (false);
}
}
/**
* Return the name operation name. This call is only valid for
* OP_NAME_FIRSTUPDATE or OP_NAME_UPDATE.
* @return The name operation's name.
*/
inline const valtype&
getOpName () const
{
switch (op)
{
case OP_KEVA_PUT:
return args[0];
default:
assert (false);
}
}
/**
* Return the name operation value. This call is only valid for
* OP_NAME_FIRSTUPDATE or OP_NAME_UPDATE.
* @return The name operation's value.
*/
inline const valtype&
getOpValue () const
{
switch (op)
{
case OP_KEVA_PUT:
// args[1] is namespace
return args[2];
default:
assert (false);
}
}
/**
* Return the name operation's rand value. This is only valid
* for OP_NAME_FIRSTUPDATE.
* @return The name operation's rand.
*/
inline const valtype&
getOpRand () const
{
assert (op == OP_NAME_FIRSTUPDATE);
return args[1];
}
/**
* Return the name operation's hash value. This is only valid
* for OP_NAME_NEW.
* @return The name operation's hash.
*/
inline const valtype&
getOpHash () const
{
assert (op == OP_NAME_NEW);
return args[0];
}
/**
* Check if the given script is a name script. This is a utility method.
* @param script The script to parse.
* @return True iff it is a name script.
*/
static inline bool
isNameScript (const CScript& script)
{
const CKevaScript op(script);
return op.isNameOp ();
}
/**
* Build a KEVA_NAMESPACE transaction.
* @param addr The address script to append.
* @param hash The hash to use.
* @return The full KEVA_NAMESPACE script.
*/
static CScript buildKevaNamespace(const CScript& addr, const valtype& nameSpace,
const valtype& displayName);
/**
* Build a KEVA_PUT transaction.
* @param addr The address script to append.
* @param hash The hash to use.
* @return The full KEVA_PUT script.
*/
static CScript buildKevaPut(const CScript& addr, const valtype& nameSpace,
const valtype& key, const valtype& value);
};
#endif // H_BITCOIN_SCRIPT_KEVA

2
src/script/script.h

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

80
src/wallet/rpckeva.cpp

@ -22,6 +22,78 @@
#include <univalue.h> #include <univalue.h>
/* ************************************************************************** */
UniValue
keva_namespace (const JSONRPCRequest& request)
{
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable (pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size () != 1)
throw std::runtime_error (
"keva_namespace \"display_name\"\n"
"\nStart creation of the given namespace.\n"
+ HelpRequiringPassphrase (pwallet) +
"\nArguments:\n"
"1. \"display_name\" (string, required) the display name of the namespace\n"
"\nResult:\n"
"[\n"
" xxxxx, (string) the txid, required for keva_put\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli ("keva_namespace", "\"display name\"")
);
RPCTypeCheck (request.params, {UniValue::VSTR});
ObserveSafeMode ();
const std::string nameStr = request.params[0].get_str ();
const valtype name = ValtypeFromString (nameStr);
if (name.size () > MAX_NAME_LENGTH)
throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
/* No explicit locking should be necessary. CReserveKey takes care
of locking the wallet, and CommitTransaction (called when sending
the tx) locks cs_main as necessary. */
EnsureWalletIsUnlocked (pwallet);
CReserveKey keyName(pwallet);
CPubKey pubKey;
const bool ok = keyName.GetReservedKey (pubKey, true);
assert (ok);
const CScript addrName = GetScriptForDestination (pubKey.GetID ());
const CScript newScript = CNameScript::buildNameNew (addrName, hash);
valtype rand(20);
GetRandBytes (&rand[0], rand.size ());
valtype toHash(rand);
toHash.insert (toHash.end (), name.begin (), name.end ());
const uint160 hash = Hash160 (toHash);
CCoinControl coinControl;
CWalletTx wtx;
SendMoneyToScript (pwallet, newScript, nullptr,
NAME_LOCKED_AMOUNT, false, wtx, coinControl);
keyName.KeepKey ();
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 ());
UniValue res(UniValue::VARR);
res.push_back (txid);
res.push_back (randStr);
return res;
}
/* ************************************************************************** */ /* ************************************************************************** */
UniValue UniValue
@ -32,7 +104,7 @@ keva_put (const JSONRPCRequest& request)
return NullUniValue; return NullUniValue;
if (request.fHelp if (request.fHelp
|| (request.params.size () != 3 && request.params.size () != 4)) || (request.params.size () != 3))
throw std::runtime_error ( throw std::runtime_error (
"keva_put \"namespace\" \"key\" \"value\" (\"create_namespace\")\n" "keva_put \"namespace\" \"key\" \"value\" (\"create_namespace\")\n"
"\nUpdate a name and possibly transfer it.\n" "\nUpdate a name and possibly transfer it.\n"
@ -40,17 +112,15 @@ keva_put (const JSONRPCRequest& request)
"\nArguments:\n" "\nArguments:\n"
"1. \"namespace\" (string, required) the namespace to insert the key to\n" "1. \"namespace\" (string, required) the namespace to insert the key to\n"
"2. \"key\" (string, required) value for the key\n" "2. \"key\" (string, required) value for the key\n"
"4. \"value\" (string, required) value for the name\n" "3. \"value\" (string, required) value for the name\n"
"5. \"create_namespace\" (boolean, optional, default=false) create the namespace if it does not exist yet\n"
"\nResult:\n" "\nResult:\n"
"\"txid\" (string) the keva_put's txid\n" "\"txid\" (string) the keva_put's txid\n"
"\nExamples:\n" "\nExamples:\n"
+ HelpExampleCli ("keva_put", "\"mynamespace\", \"new-key\", \"new-value\"") + HelpExampleCli ("keva_put", "\"mynamespace\", \"new-key\", \"new-value\"")
+ HelpExampleCli ("keva_put", "\"mynamespace\", \"new-key\", \"new-value\", true")
); );
RPCTypeCheck (request.params, RPCTypeCheck (request.params,
{UniValue::VSTR, UniValue::VSTR, UniValue::VSTR, UniValue::VSTR, UniValue::VBOOL}); {UniValue::VSTR, UniValue::VSTR, UniValue::VSTR, UniValue::VSTR});
ObserveSafeMode (); ObserveSafeMode ();

Loading…
Cancel
Save