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. 2
      src/keva/common.cpp
  2. 38
      src/keva/main.cpp
  3. 1
      src/script/interpreter.cpp
  4. 15
      src/script/keva.cpp
  5. 37
      src/script/keva.h
  6. 2
      src/script/script.h
  7. 5
      src/txmempool.h
  8. 116
      src/wallet/rpckeva.cpp
  9. 2
      src/wallet/rpcwallet.cpp

2
src/keva/common.cpp

@ -22,7 +22,9 @@ CKevaData::fromScript (unsigned h, const COutPoint& out,
const CKevaScript& script) const CKevaScript& script)
{ {
if (script.isAnyUpdate()) { if (script.isAnyUpdate()) {
if (!script.isDelete()) {
value = script.getOpValue(); value = script.getOpValue();
}
} else if (script.isNamespaceRegistration()) { } else if (script.isNamespaceRegistration()) {
value = script.getOpNamespaceDisplayName(); value = script.getOpNamespaceDisplayName();
} else { } else {

38
src/keva/main.cpp

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

1
src/script/interpreter.cpp

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

15
src/script/keva.cpp

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

37
src/script/keva.h

@ -66,6 +66,9 @@ public:
case OP_KEVA_NAMESPACE: case OP_KEVA_NAMESPACE:
return true; return true;
case OP_KEVA_DELETE:
return true;
case OP_NOP: case OP_NOP:
return false; return false;
@ -94,6 +97,9 @@ public:
case OP_KEVA_PUT: case OP_KEVA_PUT:
return op; return op;
case OP_KEVA_DELETE:
return op;
case OP_KEVA_NAMESPACE: case OP_KEVA_NAMESPACE:
return op; return op;
@ -115,6 +121,9 @@ public:
case OP_KEVA_PUT: case OP_KEVA_PUT:
return false; return false;
case OP_KEVA_DELETE:
return false;
default: default:
assert(false); assert(false);
} }
@ -133,6 +142,9 @@ public:
case OP_KEVA_PUT: case OP_KEVA_PUT:
return true; return true;
case OP_KEVA_DELETE:
return true;
default: default:
assert(false); assert(false);
} }
@ -149,6 +161,9 @@ public:
case OP_KEVA_PUT: case OP_KEVA_PUT:
return args[0]; return args[0];
case OP_KEVA_DELETE:
return args[0];
case OP_KEVA_NAMESPACE: case OP_KEVA_NAMESPACE:
return args[0]; return args[0];
@ -184,6 +199,9 @@ public:
case OP_KEVA_PUT: case OP_KEVA_PUT:
return args[1]; return args[1];
case OP_KEVA_DELETE:
return args[1];
default: default:
assert(false); assert(false);
} }
@ -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. * Check if the given script is a keva script. This is a utility method.
* @param script The script to parse. * @param script The script to parse.
@ -237,6 +265,15 @@ public:
const valtype& key, const valtype& value); 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); 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
// Keva // Keva
OP_KEVA_PUT=0xd0, OP_KEVA_PUT=0xd0,
OP_KEVA_NAMESPACE=0xd1, OP_KEVA_NAMESPACE=0xd1,
OP_KEVA_DELETE=0xd2,
// template matching params // template matching params
OP_SMALLINTEGER = 0xfa, OP_SMALLINTEGER = 0xfa,

5
src/txmempool.h

@ -142,6 +142,11 @@ public:
return kevaOp.isKevaOp() && kevaOp.getKevaOp() == OP_KEVA_PUT; 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 inline const valtype& getNamespace() const
{ {
return kevaOp.getOpNamespace(); return kevaOp.getOpNamespace();

116
src/wallet/rpckeva.cpp

@ -42,8 +42,8 @@ UniValue keva_namespace(const JSONRPCRequest& request)
"1. \"display_name\" (string, required) the display name of the namespace\n" "1. \"display_name\" (string, required) the display name of the namespace\n"
"\nResult:\n" "\nResult:\n"
"[\n" "[\n"
" xxxxx, (string) the txid, required for keva_put\n" " xxxxx, (string) the txid\n"
" xxxxx, (string) the unique namespace id, required for keva_put\n" " xxxxx, (string) the unique namespace id\n"
"]\n" "]\n"
"\nExamples:\n" "\nExamples:\n"
+ HelpExampleCli ("keva_namespace", "\"display name\"") + HelpExampleCli ("keva_namespace", "\"display name\"")
@ -96,8 +96,11 @@ UniValue keva_namespace(const JSONRPCRequest& request)
kevaNamespaceBase58.c_str(), displayNameStr.c_str(), txid.c_str()); kevaNamespaceBase58.c_str(), displayNameStr.c_str(), txid.c_str());
UniValue res(UniValue::VARR); UniValue res(UniValue::VARR);
res.push_back(txid); UniValue obj(UniValue::VOBJ);
res.push_back(kevaNamespaceBase58); obj.pushKV("txid", txid);
obj.pushKV("namespaceId", kevaNamespaceBase58);
res.push_back(obj);
return res; return res;
} }
@ -207,8 +210,8 @@ UniValue keva_put(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 3) { if (request.fHelp || 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\"\n"
"\nUpdate a name and possibly transfer it.\n" "\nInsert or update a key value pair in the given namespace.\n"
+ HelpRequiringPassphrase (pwallet) + + HelpRequiringPassphrase (pwallet) +
"\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"
@ -251,7 +254,7 @@ UniValue keva_put(const JSONRPCRequest& request)
COutput output; COutput output;
std::string kevaNamespce = namespaceStr; std::string kevaNamespce = namespaceStr;
if (!pwallet->FindKevaCoin(output, kevaNamespce)) { 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 COutPoint outp(output.tx->GetHash(), output.i);
const CTxIn txIn(outp); const CTxIn txIn(outp);
@ -281,6 +284,96 @@ UniValue keva_put(const JSONRPCRequest& request)
return wtx.GetHash().GetHex(); 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) UniValue keva_get(const JSONRPCRequest& request)
{ {
CWallet* const pwallet = GetWalletForJSONRPCRequest(request); CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
@ -392,6 +485,7 @@ UniValue keva_pending(const JSONRPCRequest& request)
UniValue arr(UniValue::VARR); UniValue arr(UniValue::VARR);
const std::string opKevaNamepsace = "keva_namespace"; const std::string opKevaNamepsace = "keva_namespace";
const std::string opKevaPut = "keva_put"; const std::string opKevaPut = "keva_put";
const std::string opKevaDelete = "keva_delete";
for (auto entry: unconfirmedNamespaces) { for (auto entry: unconfirmedNamespaces) {
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
@ -404,11 +498,19 @@ UniValue keva_pending(const JSONRPCRequest& request)
for (auto entry: unconfirmedKeyValueList) { for (auto entry: unconfirmedKeyValueList) {
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
const valtype val = std::get<2>(entry);
if (val.size() > 0) {
obj.pushKV("op", opKevaPut); obj.pushKV("op", opKevaPut);
obj.pushKV("namespace", EncodeBase58Check(std::get<0>(entry))); obj.pushKV("namespace", EncodeBase58Check(std::get<0>(entry)));
obj.pushKV("key", ValtypeToString(std::get<1>(entry))); obj.pushKV("key", ValtypeToString(std::get<1>(entry)));
obj.pushKV("value", ValtypeToString(std::get<2>(entry))); obj.pushKV("value", ValtypeToString(std::get<2>(entry)));
obj.pushKV("txid", std::get<3>(entry).ToString()); 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); arr.push_back(obj);
} }

2
src/wallet/rpcwallet.cpp

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

Loading…
Cancel
Save