2018-09-17 17:06:47 -07:00
|
|
|
// Copyright (c) 2018 Jianping Wu
|
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
#include "base58.h"
|
|
|
|
#include "coins.h"
|
|
|
|
#include "init.h"
|
|
|
|
#include "keva/common.h"
|
|
|
|
#include "keva/main.h"
|
|
|
|
#include "primitives/transaction.h"
|
|
|
|
#include "random.h"
|
|
|
|
#include "rpc/mining.h"
|
|
|
|
#include "rpc/safemode.h"
|
|
|
|
#include "rpc/server.h"
|
2018-10-25 13:55:01 -07:00
|
|
|
#include "script/keva.h"
|
2018-09-17 17:06:47 -07:00
|
|
|
#include "txmempool.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "validation.h"
|
|
|
|
#include "wallet/coincontrol.h"
|
|
|
|
#include "wallet/wallet.h"
|
|
|
|
|
|
|
|
#include <univalue.h>
|
|
|
|
|
2018-11-19 17:04:39 -08:00
|
|
|
const int NAMESPACE_LENGTH = 21;
|
|
|
|
const std::string DUMMY_NAMESPACE = "___DUMMY_NAMESPACE___";
|
2018-11-02 16:44:55 -07:00
|
|
|
|
2018-09-17 17:06:47 -07:00
|
|
|
|
2018-09-18 16:08:17 -07:00
|
|
|
/* ************************************************************************** */
|
|
|
|
|
2018-10-25 17:32:34 -07:00
|
|
|
UniValue keva_namespace(const JSONRPCRequest& request)
|
2018-09-18 16:08:17 -07:00
|
|
|
{
|
|
|
|
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"
|
2018-10-29 12:21:06 -07:00
|
|
|
"\nRegister a namespace with the given display name.\n"
|
2018-09-18 16:08:17 -07:00
|
|
|
+ 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"
|
2018-10-29 12:21:06 -07:00
|
|
|
" xxxxx, (string) the unique namespace id, required for keva_put\n"
|
2018-09-18 16:08:17 -07:00
|
|
|
"]\n"
|
|
|
|
"\nExamples:\n"
|
|
|
|
+ HelpExampleCli ("keva_namespace", "\"display name\"")
|
|
|
|
);
|
|
|
|
|
|
|
|
RPCTypeCheck (request.params, {UniValue::VSTR});
|
|
|
|
|
|
|
|
ObserveSafeMode ();
|
|
|
|
|
2018-10-25 17:32:34 -07:00
|
|
|
const std::string displayNameStr = request.params[0].get_str();
|
2018-10-17 15:39:37 -07:00
|
|
|
const valtype displayName = ValtypeFromString (displayNameStr);
|
2018-10-26 17:19:51 -07:00
|
|
|
if (displayName.size () > MAX_NAMESPACE_LENGTH) {
|
2018-09-18 16:08:17 -07:00
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
|
2018-10-26 17:19:51 -07:00
|
|
|
}
|
2018-09-18 16:08:17 -07:00
|
|
|
|
|
|
|
/* 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;
|
2018-10-20 11:51:22 -07:00
|
|
|
const bool ok = keyName.GetReservedKey(pubKey, true);
|
2018-09-18 16:08:17 -07:00
|
|
|
assert (ok);
|
|
|
|
|
2018-10-25 17:32:34 -07:00
|
|
|
CKeyID keyId = pubKey.GetID();
|
2018-10-20 11:51:22 -07:00
|
|
|
|
2018-11-19 17:04:39 -08:00
|
|
|
// The namespace name is: Hash160("first txin" || displayName)
|
|
|
|
// For now we don't know the first txin, so use dummy name here.
|
|
|
|
// It will be replaced later in CreateTransaction.
|
|
|
|
valtype namespaceDummy = ToByteVector(std::string(DUMMY_NAMESPACE));
|
|
|
|
assert(namespaceDummy.size() == NAMESPACE_LENGTH);
|
2018-10-20 11:51:22 -07:00
|
|
|
|
2018-11-06 15:46:56 -08:00
|
|
|
CScript redeemScript = GetScriptForDestination(WitnessV0KeyHash(keyId));
|
|
|
|
CScriptID scriptHash = CScriptID(redeemScript);
|
|
|
|
CScript addrName = GetScriptForDestination(scriptHash);
|
2018-11-19 17:04:39 -08:00
|
|
|
const CScript newScript = CKevaScript::buildKevaNamespace(addrName, namespaceDummy, displayName);
|
2018-09-18 16:08:17 -07:00
|
|
|
|
|
|
|
CCoinControl coinControl;
|
|
|
|
CWalletTx wtx;
|
2018-11-19 17:04:39 -08:00
|
|
|
valtype kevaNamespace;
|
|
|
|
SendMoneyToScript(pwallet, newScript, nullptr, kevaNamespace,
|
2018-10-25 17:32:34 -07:00
|
|
|
KEVA_LOCKED_AMOUNT, false, wtx, coinControl);
|
2018-10-17 15:39:37 -07:00
|
|
|
keyName.KeepKey();
|
2018-09-18 16:08:17 -07:00
|
|
|
|
2018-11-19 17:04:39 -08:00
|
|
|
std::string kevaNamespaceBase58 = EncodeBase58(kevaNamespace);
|
2018-10-17 15:39:37 -07:00
|
|
|
const std::string txid = wtx.GetHash().GetHex();
|
2018-10-25 17:32:34 -07:00
|
|
|
LogPrintf("keva_namespace: namespace=%s, displayName=%s, tx=%s\n",
|
2018-11-19 17:04:39 -08:00
|
|
|
kevaNamespaceBase58.c_str(), displayNameStr.c_str(), txid.c_str());
|
2018-09-18 16:08:17 -07:00
|
|
|
|
|
|
|
UniValue res(UniValue::VARR);
|
2018-10-17 15:39:37 -07:00
|
|
|
res.push_back(txid);
|
2018-11-19 17:04:39 -08:00
|
|
|
res.push_back(kevaNamespaceBase58);
|
2018-09-18 16:08:17 -07:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-11-02 14:15:08 -07:00
|
|
|
UniValue keva_list_namespaces(const JSONRPCRequest& request)
|
2018-09-17 17:06:47 -07:00
|
|
|
{
|
|
|
|
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
|
|
|
|
if (!EnsureWalletIsAvailable (pwallet, request.fHelp))
|
|
|
|
return NullUniValue;
|
|
|
|
|
2018-11-02 14:15:08 -07:00
|
|
|
if (request.fHelp)
|
|
|
|
throw std::runtime_error (
|
|
|
|
"keva_list_namespace\n"
|
|
|
|
"\nList all namespaces.\n"
|
|
|
|
+ HelpRequiringPassphrase(pwallet) +
|
|
|
|
"\nArguments:\n"
|
|
|
|
"\nResult:\n"
|
|
|
|
"[\n"
|
|
|
|
" xxxxx: display_name (string) namespace id : (string) display name\n"
|
|
|
|
" ...\n"
|
|
|
|
"]\n"
|
|
|
|
"\nExamples:\n"
|
|
|
|
+ HelpExampleCli("keva_list_namespace", "")
|
|
|
|
);
|
|
|
|
|
|
|
|
RPCTypeCheck (request.params, {UniValue::VSTR});
|
|
|
|
|
|
|
|
ObserveSafeMode ();
|
|
|
|
|
2018-11-02 16:44:55 -07:00
|
|
|
std::map<std::string, std::string> mapObjects;
|
2018-11-02 14:15:08 -07:00
|
|
|
{
|
|
|
|
LOCK2 (cs_main, pwallet->cs_wallet);
|
|
|
|
for (const auto& item : pwallet->mapWallet)
|
|
|
|
{
|
|
|
|
const CWalletTx& tx = item.second;
|
|
|
|
if (!tx.tx->IsKevacoin ())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
CKevaScript kevaOp;
|
|
|
|
int nOut = -1;
|
|
|
|
for (unsigned i = 0; i < tx.tx->vout.size (); ++i)
|
|
|
|
{
|
|
|
|
const CKevaScript cur(tx.tx->vout[i].scriptPubKey);
|
|
|
|
if (cur.isKevaOp ())
|
|
|
|
{
|
|
|
|
if (nOut != -1) {
|
|
|
|
LogPrintf ("ERROR: wallet contains tx with multiple name outputs");
|
|
|
|
} else {
|
|
|
|
kevaOp = cur;
|
|
|
|
nOut = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nOut == -1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!kevaOp.isNamespaceRegistration() && !kevaOp.isAnyUpdate()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-11-02 16:44:55 -07:00
|
|
|
const valtype nameSpace = kevaOp.getOpNamespace();
|
2018-11-06 15:46:56 -08:00
|
|
|
const std::string nameSpaceStr = EncodeBase58(nameSpace);
|
2018-11-02 14:15:08 -07:00
|
|
|
const CBlockIndex* pindex;
|
|
|
|
const int depth = tx.GetDepthInMainChain(pindex);
|
|
|
|
if (depth <= 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool mine = IsMine(*pwallet, kevaOp.getAddress ());
|
|
|
|
CKevaData data;
|
|
|
|
if (mine && pcoinsTip->GetNamespace(nameSpace, data)) {
|
|
|
|
std::string displayName = ValtypeToString(data.getValue());
|
2018-11-02 16:44:55 -07:00
|
|
|
mapObjects[nameSpaceStr] = displayName;
|
2018-11-02 14:15:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UniValue res(UniValue::VARR);
|
|
|
|
for (const auto& item : mapObjects) {
|
2018-11-02 16:44:55 -07:00
|
|
|
res.push_back(item.first + " : " + item.second);
|
2018-11-02 14:15:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
UniValue keva_put(const JSONRPCRequest& request)
|
|
|
|
{
|
|
|
|
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
|
|
|
|
if (!EnsureWalletIsAvailable (pwallet, request.fHelp)) {
|
|
|
|
return NullUniValue;
|
|
|
|
}
|
|
|
|
|
2018-11-02 14:33:50 -07:00
|
|
|
if (request.fHelp || request.params.size() != 3) {
|
2018-09-17 17:06:47 -07:00
|
|
|
throw std::runtime_error (
|
|
|
|
"keva_put \"namespace\" \"key\" \"value\" (\"create_namespace\")\n"
|
|
|
|
"\nUpdate a name and possibly transfer it.\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"
|
2018-09-18 16:08:17 -07:00
|
|
|
"3. \"value\" (string, required) value for the name\n"
|
2018-09-17 17:06:47 -07:00
|
|
|
"\nResult:\n"
|
|
|
|
"\"txid\" (string) the keva_put's txid\n"
|
|
|
|
"\nExamples:\n"
|
|
|
|
+ HelpExampleCli ("keva_put", "\"mynamespace\", \"new-key\", \"new-value\"")
|
|
|
|
);
|
2018-11-02 14:15:08 -07:00
|
|
|
}
|
2018-09-17 17:06:47 -07:00
|
|
|
|
2018-11-02 14:33:50 -07:00
|
|
|
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VSTR, UniValue::VSTR});
|
|
|
|
RPCTypeCheckArgument(request.params[0], UniValue::VSTR);
|
|
|
|
RPCTypeCheckArgument(request.params[1], UniValue::VSTR);
|
|
|
|
RPCTypeCheckArgument(request.params[2], UniValue::VSTR);
|
|
|
|
|
2018-09-17 17:06:47 -07:00
|
|
|
ObserveSafeMode ();
|
|
|
|
|
2018-11-06 15:46:56 -08:00
|
|
|
const std::string namespaceStr = request.params[0].get_str();
|
|
|
|
valtype nameSpace;
|
|
|
|
if (!DecodeBase58(namespaceStr, nameSpace)) {
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "failed to decode namespace");
|
|
|
|
}
|
2018-10-25 17:32:34 -07:00
|
|
|
if (nameSpace.size () > MAX_NAMESPACE_LENGTH)
|
2018-09-17 17:06:47 -07:00
|
|
|
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");
|
|
|
|
|
|
|
|
const std::string valueStr = request.params[2].get_str ();
|
|
|
|
const valtype value = ValtypeFromString (valueStr);
|
|
|
|
if (value.size () > MAX_VALUE_LENGTH)
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the value 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);
|
2018-11-03 13:42:50 -07:00
|
|
|
if (mempool.updatesNamespace(nameSpace)) {
|
2018-09-17 17:06:47 -07:00
|
|
|
throw JSONRPCError (RPC_TRANSACTION_ERROR,
|
|
|
|
"there is already a pending update for this name");
|
2018-10-25 17:32:34 -07:00
|
|
|
}
|
2018-09-17 17:06:47 -07:00
|
|
|
}
|
|
|
|
|
2018-10-25 17:32:34 -07:00
|
|
|
CKevaData oldData;
|
2018-09-17 17:06:47 -07:00
|
|
|
{
|
|
|
|
LOCK (cs_main);
|
2018-11-01 14:35:55 -07:00
|
|
|
if (!pcoinsTip->GetNamespace(nameSpace, oldData)) {
|
2018-09-17 17:06:47 -07:00
|
|
|
throw JSONRPCError (RPC_TRANSACTION_ERROR,
|
2018-10-25 17:32:34 -07:00
|
|
|
"this name can not be updated");
|
|
|
|
}
|
2018-09-17 17:06:47 -07:00
|
|
|
}
|
|
|
|
|
2018-11-01 14:35:55 -07:00
|
|
|
const COutPoint outp = oldData.getUpdateOutpoint();
|
2018-09-17 17:06:47 -07:00
|
|
|
const CTxIn txIn(outp);
|
|
|
|
|
|
|
|
/* No more locking required, similarly to name_new. */
|
|
|
|
|
2018-11-01 14:35:55 -07:00
|
|
|
EnsureWalletIsUnlocked(pwallet);
|
2018-09-17 17:06:47 -07:00
|
|
|
|
|
|
|
CReserveKey keyName(pwallet);
|
|
|
|
CPubKey pubKeyReserve;
|
2018-11-01 14:35:55 -07:00
|
|
|
const bool ok = keyName.GetReservedKey(pubKeyReserve, true);
|
|
|
|
assert(ok);
|
2018-09-17 17:06:47 -07:00
|
|
|
bool usedKey = false;
|
|
|
|
|
|
|
|
CScript addrName;
|
2018-11-01 14:35:55 -07:00
|
|
|
usedKey = true;
|
|
|
|
addrName = GetScriptForDestination(pubKeyReserve.GetID());
|
2018-09-17 17:06:47 -07:00
|
|
|
|
2018-11-01 14:35:55 -07:00
|
|
|
const CScript kevaScript = CKevaScript::buildKevaPut(addrName, nameSpace, key, value);
|
2018-09-17 17:06:47 -07:00
|
|
|
|
|
|
|
CCoinControl coinControl;
|
|
|
|
CWalletTx wtx;
|
2018-11-19 17:04:39 -08:00
|
|
|
valtype empty;
|
|
|
|
SendMoneyToScript(pwallet, kevaScript, &txIn, empty,
|
2018-10-25 17:32:34 -07:00
|
|
|
KEVA_LOCKED_AMOUNT, false, wtx, coinControl);
|
2018-09-17 17:06:47 -07:00
|
|
|
|
2018-11-01 14:35:55 -07:00
|
|
|
if (usedKey) {
|
2018-09-17 17:06:47 -07:00
|
|
|
keyName.KeepKey ();
|
2018-11-01 14:35:55 -07:00
|
|
|
}
|
2018-09-17 17:06:47 -07:00
|
|
|
|
2018-11-01 14:35:55 -07:00
|
|
|
return wtx.GetHash().GetHex();
|
2018-09-17 17:06:47 -07:00
|
|
|
}
|
2018-11-02 14:33:50 -07:00
|
|
|
|
|
|
|
UniValue keva_get(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_get \"namespace\" \"key\"\n"
|
|
|
|
"\nGet value of the given key.\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"
|
|
|
|
"\"value\" (string) the value associated with the key\n"
|
|
|
|
"\nExamples:\n"
|
|
|
|
+ HelpExampleCli ("keva_get", "\"namespace_id\", \"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 ();
|
2018-11-06 15:46:56 -08:00
|
|
|
valtype nameSpace;
|
|
|
|
if (!DecodeBase58(namespaceStr, nameSpace)) {
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "failed to decode namespace");
|
|
|
|
}
|
2018-11-02 14:33:50 -07:00
|
|
|
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;
|
|
|
|
{
|
|
|
|
LOCK (cs_main);
|
|
|
|
if (!pcoinsTip->GetName(nameSpace, key, data)) {
|
|
|
|
throw JSONRPCError (RPC_TRANSACTION_ERROR, "key not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ValtypeToString(data.getValue());
|
|
|
|
}
|