Browse Source

WIP: implemented keva_delete.

Problem: after the call, the corresponding keva coin is gone!
cn
Jianping Wu 6 years ago
parent
commit
df20cfc6d2
  1. 10
      src/keva/common.cpp
  2. 46
      src/keva/main.cpp
  3. 1
      src/script/interpreter.cpp
  4. 15
      src/script/keva.cpp
  5. 39
      src/script/keva.h
  6. 2
      src/script/script.h
  7. 5
      src/txmempool.h
  8. 126
      src/wallet/rpckeva.cpp
  9. 2
      src/wallet/rpcwallet.cpp

10
src/keva/common.cpp

@ -22,7 +22,9 @@ CKevaData::fromScript (unsigned h, const COutPoint& out, @@ -22,7 +22,9 @@ CKevaData::fromScript (unsigned h, const COutPoint& out,
const CKevaScript& script)
{
if (script.isAnyUpdate()) {
value = script.getOpValue();
if (!script.isDelete()) {
value = script.getOpValue();
}
} else if (script.isNamespaceRegistration()) {
value = script.getOpNamespaceDisplayName();
} else {
@ -212,14 +214,14 @@ CKevaCache::set(const valtype& nameSpace, const valtype& key, const CKevaData& d @@ -212,14 +214,14 @@ CKevaCache::set(const valtype& nameSpace, const valtype& key, const CKevaData& d
auto name = std::make_tuple(nameSpace, key);
const std::set<NamespaceKeyType>::iterator di = deleted.find(name);
if (di != deleted.end()) {
deleted.erase (di);
deleted.erase(di);
}
const EntryMap::iterator ei = entries.find(name);
if (ei != entries.end ())
if (ei != entries.end())
ei->second = data;
else
entries.insert (std::make_pair(name, data));
entries.insert(std::make_pair(name, data));
}
void

46
src/keva/main.cpp

@ -58,6 +58,12 @@ CKevaMemPool::addUnchecked (const uint256& hash, const CTxMemPoolEntry& entry) @@ -58,6 +58,12 @@ CKevaMemPool::addUnchecked (const uint256& hash, const CTxMemPoolEntry& entry)
const valtype& nameSpace = entry.getNamespace();
listUnconfirmedKeyValues.push_back(std::make_tuple(hash, nameSpace, entry.getKey(), entry.getValue()));
}
if (entry.isKeyDelete()) {
const valtype& nameSpace = entry.getNamespace();
const valtype& empty = ValtypeFromString("");
listUnconfirmedKeyValues.push_back(std::make_tuple(hash, nameSpace, entry.getKey(), empty));
}
}
bool
@ -113,7 +119,7 @@ void CKevaMemPool::remove(const CTxMemPoolEntry& entry) @@ -113,7 +119,7 @@ void CKevaMemPool::remove(const CTxMemPoolEntry& entry)
}
}
if (entry.isKeyUpdate()) {
if (entry.isKeyUpdate() || entry.isKeyDelete()) {
auto hash = entry.GetTx().GetHash();
for (auto iter = listUnconfirmedKeyValues.begin(); iter != listUnconfirmedKeyValues.end(); ++iter) {
if (std::get<0>(*iter) == hash) {
@ -190,6 +196,11 @@ CKevaMemPool::checkTx(const CTransaction& tx) const @@ -190,6 +196,11 @@ CKevaMemPool::checkTx(const CTransaction& tx) const
break;
}
case OP_KEVA_DELETE:
{
break;
}
default:
assert (false);
}
@ -329,19 +340,29 @@ CheckKevaTransaction (const CTransaction& tx, unsigned nHeight, @@ -329,19 +340,29 @@ CheckKevaTransaction (const CTransaction& tx, unsigned nHeight,
return state.Invalid (error ("CheckKevaTransaction: key too long"));
}
if (nameOpOut.getOpValue().size () > MAX_VALUE_LENGTH) {
return state.Invalid (error ("CheckKevaTransaction: value too long"));
const valtype& nameSpace = nameOpOut.getOpNamespace();
if (nameSpace != nameOpIn.getOpNamespace()) {
return state.Invalid(error("%s: KEVA_PUT namespace mismatch to prev tx found in %s", __func__, txid));
}
/* Process KEVA_PUT next. */
const valtype& nameSpace = nameOpOut.getOpNamespace();
if (nameOpOut.getKevaOp() == OP_KEVA_PUT) {
if (nameOpOut.getOpValue().size () > MAX_VALUE_LENGTH) {
return state.Invalid (error ("CheckKevaTransaction: value too long"));
}
if (!nameOpIn.isAnyUpdate() && !nameOpIn.isNamespaceRegistration()) {
return state.Invalid(error("CheckKevaTransaction: KEVA_PUT with prev input that is no update"));
}
}
if (nameSpace != nameOpIn.getOpNamespace()) {
return state.Invalid(error("%s: KEVA_PUT namespace mismatch to prev tx found in %s", __func__, txid));
if (nameOpOut.getKevaOp() == OP_KEVA_DELETE) {
if (!nameOpIn.isAnyUpdate() && !nameOpIn.isNamespaceRegistration()) {
return state.Invalid(error("CheckKevaTransaction: KEVA_DELETE with prev input that is no update"));
}
CKevaData data;
const bool hasKey = view.GetName(nameSpace, nameOpOut.getOpKey(), data);
if (!hasKey) {
return state.Invalid(error("CheckKevaTransaction: no key to delete"));
}
}
return true;
@ -351,7 +372,7 @@ void ApplyKevaTransaction(const CTransaction& tx, unsigned nHeight, @@ -351,7 +372,7 @@ void ApplyKevaTransaction(const CTransaction& tx, unsigned nHeight,
CCoinsViewCache& view, CBlockUndo& undo)
{
assert (nHeight != MEMPOOL_HEIGHT);
if (!tx.IsKevacoin ())
if (!tx.IsKevacoin())
return;
/* Changes are encoded in the outputs. We don't have to do any checks,
@ -362,7 +383,6 @@ void ApplyKevaTransaction(const CTransaction& tx, unsigned nHeight, @@ -362,7 +383,6 @@ void ApplyKevaTransaction(const CTransaction& tx, unsigned nHeight,
if (!op.isKevaOp()) {
continue;
}
if (op.isNamespaceRegistration()) {
const valtype& nameSpace = op.getOpNamespace();
const valtype& displayName = op.getOpNamespaceDisplayName();
@ -389,8 +409,12 @@ void ApplyKevaTransaction(const CTransaction& tx, unsigned nHeight, @@ -389,8 +409,12 @@ void ApplyKevaTransaction(const CTransaction& tx, unsigned nHeight,
undo.vkevaundo.push_back(opUndo);
CKevaData data;
data.fromScript(nHeight, COutPoint(tx.GetHash(), i), op);
view.SetName(nameSpace, key, data, false);
if (op.isDelete()) {
view.DeleteName(nameSpace, key);
} else {
data.fromScript(nHeight, COutPoint(tx.GetHash(), i), op);
view.SetName(nameSpace, key, data, false);
}
}
}
}

1
src/script/interpreter.cpp

@ -359,6 +359,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& @@ -359,6 +359,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
//
case OP_KEVA_NAMESPACE:
case OP_KEVA_PUT:
case OP_KEVA_DELETE:
break;
case OP_CHECKLOCKTIMEVERIFY:

15
src/script/keva.cpp

@ -55,6 +55,12 @@ CKevaScript::CKevaScript (const CScript& script) @@ -55,6 +55,12 @@ CKevaScript::CKevaScript (const CScript& script)
}
break;
case OP_KEVA_DELETE:
if (args.size() != 2) {
return;
}
break;
case OP_KEVA_NAMESPACE:
if (args.size() != 2) {
return;
@ -79,6 +85,15 @@ CKevaScript::buildKevaPut(const CScript& addr, const valtype& nameSpace, @@ -79,6 +85,15 @@ CKevaScript::buildKevaPut(const CScript& addr, const valtype& nameSpace,
return prefix + addr;
}
CScript
CKevaScript::buildKevaDelete(const CScript& addr, const valtype& nameSpace, const valtype& key)
{
CScript prefix;
prefix << OP_KEVA_DELETE << nameSpace << key << OP_2DROP;
return prefix + addr;
}
CScript CKevaScript::buildKevaNamespace(const CScript& addr, const valtype& nameSpace,
const valtype& displayName)
{

39
src/script/keva.h

@ -66,6 +66,9 @@ public: @@ -66,6 +66,9 @@ public:
case OP_KEVA_NAMESPACE:
return true;
case OP_KEVA_DELETE:
return true;
case OP_NOP:
return false;
@ -94,6 +97,9 @@ public: @@ -94,6 +97,9 @@ public:
case OP_KEVA_PUT:
return op;
case OP_KEVA_DELETE:
return op;
case OP_KEVA_NAMESPACE:
return op;
@ -115,6 +121,9 @@ public: @@ -115,6 +121,9 @@ public:
case OP_KEVA_PUT:
return false;
case OP_KEVA_DELETE:
return false;
default:
assert(false);
}
@ -133,6 +142,9 @@ public: @@ -133,6 +142,9 @@ public:
case OP_KEVA_PUT:
return true;
case OP_KEVA_DELETE:
return true;
default:
assert(false);
}
@ -149,6 +161,9 @@ public: @@ -149,6 +161,9 @@ public:
case OP_KEVA_PUT:
return args[0];
case OP_KEVA_DELETE:
return args[0];
case OP_KEVA_NAMESPACE:
return args[0];
@ -184,6 +199,9 @@ public: @@ -184,6 +199,9 @@ public:
case OP_KEVA_PUT:
return args[1];
case OP_KEVA_DELETE:
return args[1];
default:
assert(false);
}
@ -206,6 +224,16 @@ public: @@ -206,6 +224,16 @@ public:
}
}
/**
* Return the keva operation value. This call is only valid for
* OP_KEVA_PUT.
* @return The keva operation's value.
*/
inline bool isDelete() const
{
return (op == OP_KEVA_DELETE);
}
/**
* Check if the given script is a keva script. This is a utility method.
* @param script The script to parse.
@ -215,7 +243,7 @@ public: @@ -215,7 +243,7 @@ public:
isKevaScript (const CScript& script)
{
const CKevaScript op(script);
return op.isKevaOp ();
return op.isKevaOp();
}
/**
@ -237,6 +265,15 @@ public: @@ -237,6 +265,15 @@ public:
const valtype& key, const valtype& value);
/**
* Build a KEVA_DELETE transaction.
* @param addr The address script to append.
* @param hash The hash to use.
* @return The full KEVA_DELETE script.
*/
static CScript buildKevaDelete(const CScript& addr, const valtype& nameSpace, const valtype& key);
static CScript replaceKevaNamespace(const CScript& oldScript, const uint256& txId, valtype& kaveNamespace, const CChainParams& params);
};

2
src/script/script.h

@ -186,7 +186,7 @@ enum opcodetype @@ -186,7 +186,7 @@ enum opcodetype
// Keva
OP_KEVA_PUT=0xd0,
OP_KEVA_NAMESPACE=0xd1,
OP_KEVA_DELETE=0xd2,
// template matching params
OP_SMALLINTEGER = 0xfa,

5
src/txmempool.h

@ -142,6 +142,11 @@ public: @@ -142,6 +142,11 @@ public:
return kevaOp.isKevaOp() && kevaOp.getKevaOp() == OP_KEVA_PUT;
}
inline bool isKeyDelete() const
{
return kevaOp.isKevaOp() && kevaOp.getKevaOp() == OP_KEVA_DELETE;
}
inline const valtype& getNamespace() const
{
return kevaOp.getOpNamespace();

126
src/wallet/rpckeva.cpp

@ -42,8 +42,8 @@ UniValue keva_namespace(const JSONRPCRequest& request) @@ -42,8 +42,8 @@ UniValue keva_namespace(const JSONRPCRequest& request)
"1. \"display_name\" (string, required) the display name of the namespace\n"
"\nResult:\n"
"[\n"
" xxxxx, (string) the txid, required for keva_put\n"
" xxxxx, (string) the unique namespace id, required for keva_put\n"
" xxxxx, (string) the txid\n"
" xxxxx, (string) the unique namespace id\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli ("keva_namespace", "\"display name\"")
@ -96,8 +96,11 @@ UniValue keva_namespace(const JSONRPCRequest& request) @@ -96,8 +96,11 @@ UniValue keva_namespace(const JSONRPCRequest& request)
kevaNamespaceBase58.c_str(), displayNameStr.c_str(), txid.c_str());
UniValue res(UniValue::VARR);
res.push_back(txid);
res.push_back(kevaNamespaceBase58);
UniValue obj(UniValue::VOBJ);
obj.pushKV("txid", txid);
obj.pushKV("namespaceId", kevaNamespaceBase58);
res.push_back(obj);
return res;
}
@ -207,8 +210,8 @@ UniValue keva_put(const JSONRPCRequest& request) @@ -207,8 +210,8 @@ UniValue keva_put(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 3) {
throw std::runtime_error (
"keva_put \"namespace\" \"key\" \"value\" (\"create_namespace\")\n"
"\nUpdate a name and possibly transfer it.\n"
"keva_put \"namespace\" \"key\" \"value\"\n"
"\nInsert or update a key value pair in the given namespace.\n"
+ HelpRequiringPassphrase (pwallet) +
"\nArguments:\n"
"1. \"namespace\" (string, required) the namespace to insert the key to\n"
@ -251,7 +254,7 @@ UniValue keva_put(const JSONRPCRequest& request) @@ -251,7 +254,7 @@ UniValue keva_put(const JSONRPCRequest& request)
COutput output;
std::string kevaNamespce = namespaceStr;
if (!pwallet->FindKevaCoin(output, kevaNamespce)) {
throw JSONRPCError (RPC_TRANSACTION_ERROR, "this name can not be updated");
throw JSONRPCError (RPC_TRANSACTION_ERROR, "this namespace can not be updated");
}
const COutPoint outp(output.tx->GetHash(), output.i);
const CTxIn txIn(outp);
@ -281,6 +284,96 @@ UniValue keva_put(const JSONRPCRequest& request) @@ -281,6 +284,96 @@ UniValue keva_put(const JSONRPCRequest& request)
return wtx.GetHash().GetHex();
}
UniValue keva_delete(const JSONRPCRequest& request)
{
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable (pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error (
"keva_delete \"namespace\" \"key\"\n"
"\nRemove the specified key from the given namespace.\n"
+ HelpRequiringPassphrase (pwallet) +
"\nArguments:\n"
"1. \"namespace\" (string, required) the namespace to insert the key to\n"
"2. \"key\" (string, required) value for the key\n"
"\nResult:\n"
"\"txid\" (string) the keva_delete's txid\n"
"\nExamples:\n"
+ HelpExampleCli ("keva_delete", "\"mynamespace\", \"key\"")
);
}
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VSTR);
RPCTypeCheckArgument(request.params[1], UniValue::VSTR);
ObserveSafeMode ();
const std::string namespaceStr = request.params[0].get_str();
valtype nameSpace;
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) {
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id");
}
if (nameSpace.size () > MAX_NAMESPACE_LENGTH) {
throw JSONRPCError (RPC_INVALID_PARAMETER, "the namespace is too long");
}
const std::string keyStr = request.params[1].get_str ();
const valtype key = ValtypeFromString (keyStr);
if (key.size () > MAX_KEY_LENGTH) {
throw JSONRPCError (RPC_INVALID_PARAMETER, "the key is too long");
}
CKevaData data;
{
LOCK2(cs_main, mempool.cs);
if (!pcoinsTip->GetName(nameSpace, key, data)) {
std::vector<std::tuple<valtype, valtype, valtype, uint256>> unconfirmedKeyValueList;
valtype val;
if (!mempool.getUnconfirmedKeyValue(nameSpace, key, val) || val.size() == 0) {
throw JSONRPCError (RPC_TRANSACTION_ERROR, "key not found");
}
}
}
EnsureWalletIsUnlocked(pwallet);
COutput output;
std::string kevaNamespce = namespaceStr;
if (!pwallet->FindKevaCoin(output, kevaNamespce)) {
throw JSONRPCError (RPC_TRANSACTION_ERROR, "this namespace can not be updated");
}
const COutPoint outp(output.tx->GetHash(), output.i);
const CTxIn txIn(outp);
CReserveKey keyName(pwallet);
CPubKey pubKeyReserve;
const bool ok = keyName.GetReservedKey(pubKeyReserve, true);
assert(ok);
bool usedKey = false;
CScript addrName;
usedKey = true;
addrName = GetScriptForDestination(pubKeyReserve.GetID());
const CScript kevaScript = CKevaScript::buildKevaDelete(addrName, nameSpace, key);
CCoinControl coinControl;
CWalletTx wtx;
valtype empty;
SendMoneyToScript(pwallet, kevaScript, &txIn, empty,
KEVA_LOCKED_AMOUNT, false, wtx, coinControl);
if (usedKey) {
keyName.KeepKey();
}
return wtx.GetHash().GetHex();
}
UniValue keva_get(const JSONRPCRequest& request)
{
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
@ -392,6 +485,7 @@ UniValue keva_pending(const JSONRPCRequest& request) @@ -392,6 +485,7 @@ UniValue keva_pending(const JSONRPCRequest& request)
UniValue arr(UniValue::VARR);
const std::string opKevaNamepsace = "keva_namespace";
const std::string opKevaPut = "keva_put";
const std::string opKevaDelete = "keva_delete";
for (auto entry: unconfirmedNamespaces) {
UniValue obj(UniValue::VOBJ);
@ -404,11 +498,19 @@ UniValue keva_pending(const JSONRPCRequest& request) @@ -404,11 +498,19 @@ UniValue keva_pending(const JSONRPCRequest& request)
for (auto entry: unconfirmedKeyValueList) {
UniValue obj(UniValue::VOBJ);
obj.pushKV("op", opKevaPut);
obj.pushKV("namespace", EncodeBase58Check(std::get<0>(entry)));
obj.pushKV("key", ValtypeToString(std::get<1>(entry)));
obj.pushKV("value", ValtypeToString(std::get<2>(entry)));
obj.pushKV("txid", std::get<3>(entry).ToString());
const valtype val = std::get<2>(entry);
if (val.size() > 0) {
obj.pushKV("op", opKevaPut);
obj.pushKV("namespace", EncodeBase58Check(std::get<0>(entry)));
obj.pushKV("key", ValtypeToString(std::get<1>(entry)));
obj.pushKV("value", ValtypeToString(std::get<2>(entry)));
obj.pushKV("txid", std::get<3>(entry).ToString());
} else {
obj.pushKV("op", opKevaDelete);
obj.pushKV("namespace", EncodeBase58Check(std::get<0>(entry)));
obj.pushKV("key", ValtypeToString(std::get<1>(entry)));
obj.pushKV("txid", std::get<3>(entry).ToString());
}
arr.push_back(obj);
}

2
src/wallet/rpcwallet.cpp

@ -3593,6 +3593,7 @@ extern UniValue rescanblockchain(const JSONRPCRequest& request); @@ -3593,6 +3593,7 @@ extern UniValue rescanblockchain(const JSONRPCRequest& request);
// in rpckeva.cpp
extern UniValue keva_namespace(const JSONRPCRequest& request);
extern UniValue keva_put(const JSONRPCRequest& request);
extern UniValue keva_delete(const JSONRPCRequest& request);
extern UniValue keva_get(const JSONRPCRequest& request);
extern UniValue keva_list_namespaces(const JSONRPCRequest& request);
extern UniValue keva_pending(const JSONRPCRequest& request);
@ -3658,6 +3659,7 @@ static const CRPCCommand commands[] = @@ -3658,6 +3659,7 @@ static const CRPCCommand commands[] =
{ "kevacoin", "keva_namespace", &keva_namespace, {"display_name"} },
{ "kevacoin", "keva_list_namespaces", &keva_list_namespaces, {} },
{ "kevacoin", "keva_put", &keva_put, {"namespace", "key", "value"} },
{ "kevacoin", "keva_delete", &keva_delete, {"namespace", "key"} },
{ "kevacoin", "keva_get", &keva_get, {"namespace", "key"} },
{ "kevacoin", "keva_pending", &keva_pending, {"namespace"} }
};

Loading…
Cancel
Save