|
|
|
@ -1,4 +1,8 @@
@@ -1,4 +1,8 @@
|
|
|
|
|
// Copyright (c) 2018 Jianping Wu
|
|
|
|
|
// Copyright (c) 2014-2017 Daniel Kraft
|
|
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
|
|
// Copyright (c) 2018 the Kevacoin Core Developers
|
|
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
|
@ -20,6 +24,7 @@
@@ -20,6 +24,7 @@
|
|
|
|
|
#include "wallet/wallet.h" |
|
|
|
|
|
|
|
|
|
#include <univalue.h> |
|
|
|
|
#include <boost/xpressive/xpressive_dynamic.hpp> |
|
|
|
|
|
|
|
|
|
const int NAMESPACE_LENGTH = 21; |
|
|
|
|
const std::string DUMMY_NAMESPACE = "___DUMMY_NAMESPACE___"; |
|
|
|
@ -33,7 +38,7 @@ UniValue keva_namespace(const JSONRPCRequest& request)
@@ -33,7 +38,7 @@ UniValue keva_namespace(const JSONRPCRequest& request)
|
|
|
|
|
if (!EnsureWalletIsAvailable (pwallet, request.fHelp)) |
|
|
|
|
return NullUniValue; |
|
|
|
|
|
|
|
|
|
if (request.fHelp || request.params.size () != 1) |
|
|
|
|
if (request.fHelp || request.params.size() != 1) |
|
|
|
|
throw std::runtime_error ( |
|
|
|
|
"keva_namespace \"display_name\"\n" |
|
|
|
|
"\nRegister a namespace with the given display name.\n" |
|
|
|
@ -55,7 +60,7 @@ UniValue keva_namespace(const JSONRPCRequest& request)
@@ -55,7 +60,7 @@ UniValue keva_namespace(const JSONRPCRequest& request)
|
|
|
|
|
|
|
|
|
|
const std::string displayNameStr = request.params[0].get_str(); |
|
|
|
|
const valtype displayName = ValtypeFromString (displayNameStr); |
|
|
|
|
if (displayName.size () > MAX_NAMESPACE_LENGTH) { |
|
|
|
|
if (displayName.size() > MAX_NAMESPACE_LENGTH) { |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -140,7 +145,7 @@ UniValue keva_list_namespaces(const JSONRPCRequest& request)
@@ -140,7 +145,7 @@ UniValue keva_list_namespaces(const JSONRPCRequest& request)
|
|
|
|
|
|
|
|
|
|
CKevaScript kevaOp; |
|
|
|
|
int nOut = -1; |
|
|
|
|
for (unsigned i = 0; i < tx.tx->vout.size (); ++i) |
|
|
|
|
for (unsigned i = 0; i < tx.tx->vout.size(); ++i) |
|
|
|
|
{ |
|
|
|
|
const CKevaScript cur(tx.tx->vout[i].scriptPubKey); |
|
|
|
|
if (cur.isKevaOp ()) |
|
|
|
@ -236,17 +241,17 @@ UniValue keva_put(const JSONRPCRequest& request)
@@ -236,17 +241,17 @@ UniValue keva_put(const JSONRPCRequest& request)
|
|
|
|
|
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) { |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id"); |
|
|
|
|
} |
|
|
|
|
if (nameSpace.size () > MAX_NAMESPACE_LENGTH) |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
if (value.size() > MAX_VALUE_LENGTH) |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the value is too long"); |
|
|
|
|
|
|
|
|
|
EnsureWalletIsUnlocked(pwallet); |
|
|
|
@ -317,13 +322,13 @@ UniValue keva_delete(const JSONRPCRequest& request)
@@ -317,13 +322,13 @@ UniValue keva_delete(const JSONRPCRequest& request)
|
|
|
|
|
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) { |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id"); |
|
|
|
|
} |
|
|
|
|
if (nameSpace.size () > MAX_NAMESPACE_LENGTH) { |
|
|
|
|
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) { |
|
|
|
|
if (key.size() > MAX_KEY_LENGTH) { |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the key is too long"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -414,12 +419,12 @@ UniValue keva_get(const JSONRPCRequest& request)
@@ -414,12 +419,12 @@ UniValue keva_get(const JSONRPCRequest& request)
|
|
|
|
|
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) { |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id"); |
|
|
|
|
} |
|
|
|
|
if (nameSpace.size () > MAX_NAMESPACE_LENGTH) |
|
|
|
|
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) |
|
|
|
|
if (key.size() > MAX_KEY_LENGTH) |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "the key is too long"); |
|
|
|
|
|
|
|
|
|
CKevaData data; |
|
|
|
@ -445,7 +450,7 @@ UniValue keva_get(const JSONRPCRequest& request)
@@ -445,7 +450,7 @@ UniValue keva_get(const JSONRPCRequest& request)
|
|
|
|
|
|
|
|
|
|
UniValue keva_pending(const JSONRPCRequest& request) |
|
|
|
|
{ |
|
|
|
|
if (request.fHelp || request.params.size () > 1) |
|
|
|
|
if (request.fHelp || request.params.size() > 1) |
|
|
|
|
throw std::runtime_error ( |
|
|
|
|
"keva_pending (\"namespace\")\n" |
|
|
|
|
"\nList unconfirmed keva operations in the mempool.\n" |
|
|
|
@ -523,3 +528,221 @@ UniValue keva_pending(const JSONRPCRequest& request)
@@ -523,3 +528,221 @@ UniValue keva_pending(const JSONRPCRequest& request)
|
|
|
|
|
|
|
|
|
|
return arr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the help string description to use for keva info objects. |
|
|
|
|
* @param indent Indentation at the line starts. |
|
|
|
|
* @param trailing Trailing string (e. g., comma for an array of these objects). |
|
|
|
|
* @return The description string. |
|
|
|
|
*/ |
|
|
|
|
std::string getKevaInfoHelp (const std::string& indent, const std::string& trailing) |
|
|
|
|
{ |
|
|
|
|
std::ostringstream res; |
|
|
|
|
|
|
|
|
|
res << indent << "{" << std::endl; |
|
|
|
|
res << indent << " \"name\": xxxxx, " |
|
|
|
|
<< "(string) the requested name" << std::endl; |
|
|
|
|
res << indent << " \"value\": xxxxx, " |
|
|
|
|
<< "(string) the name's current value" << std::endl; |
|
|
|
|
res << indent << " \"txid\": xxxxx, " |
|
|
|
|
<< "(string) the name's last update tx" << std::endl; |
|
|
|
|
res << indent << " \"address\": xxxxx, " |
|
|
|
|
<< "(string) the address holding the name" << std::endl; |
|
|
|
|
res << indent << " \"height\": xxxxx, " |
|
|
|
|
<< "(numeric) the name's last update height" << std::endl; |
|
|
|
|
res << indent << "}" << trailing << std::endl; |
|
|
|
|
|
|
|
|
|
return res.str (); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Utility routine to construct a "name info" object to return. This is used |
|
|
|
|
* for name_show and also name_list. |
|
|
|
|
* @param name The name. |
|
|
|
|
* @param value The name's value. |
|
|
|
|
* @param outp The last update's outpoint. |
|
|
|
|
* @param addr The name's address script. |
|
|
|
|
* @param height The name's last update height. |
|
|
|
|
* @return A JSON object to return. |
|
|
|
|
*/ |
|
|
|
|
UniValue |
|
|
|
|
getKevaInfo (const valtype& name, const valtype& value, const COutPoint& outp, |
|
|
|
|
const CScript& addr, int height) |
|
|
|
|
{ |
|
|
|
|
UniValue obj(UniValue::VOBJ); |
|
|
|
|
obj.pushKV("name", ValtypeToString(name)); |
|
|
|
|
obj.pushKV("value", ValtypeToString(value)); |
|
|
|
|
obj.pushKV("txid", outp.hash.GetHex()); |
|
|
|
|
obj.pushKV("vout", static_cast<int>(outp.n)); |
|
|
|
|
|
|
|
|
|
/* Try to extract the address. May fail if we can't parse the script
|
|
|
|
|
as a "standard" script. */ |
|
|
|
|
CTxDestination dest; |
|
|
|
|
std::string addrStr; |
|
|
|
|
if (ExtractDestination(addr, dest)) |
|
|
|
|
addrStr = EncodeDestination(dest); |
|
|
|
|
else |
|
|
|
|
addrStr = "<nonstandard>"; |
|
|
|
|
obj.pushKV("address", addrStr); |
|
|
|
|
|
|
|
|
|
/* Calculate expiration data. */ |
|
|
|
|
const int curHeight = chainActive.Height (); |
|
|
|
|
const Consensus::Params& params = Params ().GetConsensus (); |
|
|
|
|
obj.pushKV("height", height); |
|
|
|
|
|
|
|
|
|
return obj; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return name info object for a CNameData object. |
|
|
|
|
* @param name The name. |
|
|
|
|
* @param data The name's data. |
|
|
|
|
* @return A JSON object to return. |
|
|
|
|
*/ |
|
|
|
|
UniValue |
|
|
|
|
getKevaInfo (const valtype& name, const CKevaData& data) |
|
|
|
|
{ |
|
|
|
|
return getKevaInfo(name, data.getValue (), data.getUpdateOutpoint (), |
|
|
|
|
data.getAddress (), data.getHeight ()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UniValue keva_filter(const JSONRPCRequest& request) |
|
|
|
|
{ |
|
|
|
|
if (request.fHelp || request.params.size() > 6 || request.params.size() == 0) |
|
|
|
|
throw std::runtime_error( |
|
|
|
|
"keva_filter (\"namespaceId\" (\"regexp\" (\"from\" (\"nb\" (\"stat\")))))\n" |
|
|
|
|
"\nScan and list keys matching a regular expression.\n" |
|
|
|
|
"\nArguments:\n" |
|
|
|
|
"1. \"namespace\" (string) namespace Id\n" |
|
|
|
|
"2. \"regexp\" (string, optional) filter keys with this regexp\n" |
|
|
|
|
"3. \"maxage\" (numeric, optional, default=36000) only consider names updated in the last \"maxage\" blocks; 0 means all names\n" |
|
|
|
|
"4. \"from\" (numeric, optional, default=0) return from this position onward; index starts at 0\n" |
|
|
|
|
"5. \"nb\" (numeric, optional, default=0) return only \"nb\" entries; 0 means all\n" |
|
|
|
|
"6. \"stat\" (string, optional) if set to the string \"stat\", print statistics instead of returning the names\n" |
|
|
|
|
"\nResult:\n" |
|
|
|
|
"[\n" |
|
|
|
|
+ getKevaInfoHelp (" ", ",") + |
|
|
|
|
" ...\n" |
|
|
|
|
"]\n" |
|
|
|
|
"\nExamples:\n" |
|
|
|
|
+ HelpExampleCli ("keva_filter", "\"\" 5") |
|
|
|
|
+ HelpExampleCli ("keva_filter", "\"^id/\"") |
|
|
|
|
+ HelpExampleCli ("keva_filter", "\"^id/\" 36000 0 0 \"stat\"") |
|
|
|
|
+ HelpExampleRpc ("keva_scan", "\"^d/\"") |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
RPCTypeCheck(request.params, { |
|
|
|
|
UniValue::VSTR, UniValue::VSTR, UniValue::VNUM, |
|
|
|
|
UniValue::VNUM, UniValue::VNUM, UniValue::VSTR |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (IsInitialBlockDownload()) { |
|
|
|
|
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, |
|
|
|
|
"Kevacoin is downloading blocks..."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ObserveSafeMode(); |
|
|
|
|
|
|
|
|
|
/* ********************** */ |
|
|
|
|
/* Interpret parameters. */ |
|
|
|
|
|
|
|
|
|
bool haveRegexp(false); |
|
|
|
|
boost::xpressive::sregex regexp; |
|
|
|
|
|
|
|
|
|
valtype nameSpace; |
|
|
|
|
int maxage(36000), from(0), nb(0); |
|
|
|
|
bool stats(false); |
|
|
|
|
|
|
|
|
|
if (request.params.size() >= 1) { |
|
|
|
|
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 (request.params.size() >= 2) { |
|
|
|
|
haveRegexp = true; |
|
|
|
|
regexp = boost::xpressive::sregex::compile (request.params[1].get_str()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (request.params.size() >= 3) |
|
|
|
|
maxage = request.params[2].get_int(); |
|
|
|
|
if (maxage < 0) |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, |
|
|
|
|
"'maxage' should be non-negative"); |
|
|
|
|
|
|
|
|
|
if (request.params.size() >= 4) |
|
|
|
|
from = request.params[3].get_int (); |
|
|
|
|
|
|
|
|
|
if (from < 0) |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "'from' should be non-negative"); |
|
|
|
|
|
|
|
|
|
if (request.params.size() >= 5) |
|
|
|
|
nb = request.params[4].get_int (); |
|
|
|
|
|
|
|
|
|
if (nb < 0) |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, "'nb' should be non-negative"); |
|
|
|
|
|
|
|
|
|
if (request.params.size() >= 6) { |
|
|
|
|
if (request.params[5].get_str() != "stat") |
|
|
|
|
throw JSONRPCError (RPC_INVALID_PARAMETER, |
|
|
|
|
"fifth argument must be the literal string 'stat'"); |
|
|
|
|
stats = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ******************************************* */ |
|
|
|
|
/* Iterate over names to build up the result. */ |
|
|
|
|
|
|
|
|
|
UniValue keys(UniValue::VARR); |
|
|
|
|
unsigned count(0); |
|
|
|
|
|
|
|
|
|
LOCK (cs_main); |
|
|
|
|
|
|
|
|
|
valtype key; |
|
|
|
|
CKevaData data; |
|
|
|
|
std::unique_ptr<CKevaIterator> iter(pcoinsTip->IterateKeys(nameSpace)); |
|
|
|
|
while (iter->next(key, data)) { |
|
|
|
|
const int age = chainActive.Height() - data.getHeight(); |
|
|
|
|
assert(age >= 0); |
|
|
|
|
if (maxage != 0 && age >= maxage) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
if (haveRegexp) { |
|
|
|
|
const std::string keyStr = ValtypeToString(key); |
|
|
|
|
boost::xpressive::smatch matches; |
|
|
|
|
if (!boost::xpressive::regex_search(keyStr, matches, regexp)) |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (from > 0) { |
|
|
|
|
--from; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
assert(from == 0); |
|
|
|
|
|
|
|
|
|
if (stats) |
|
|
|
|
++count; |
|
|
|
|
else |
|
|
|
|
keys.push_back(getKevaInfo(key, data)); |
|
|
|
|
|
|
|
|
|
if (nb > 0) { |
|
|
|
|
--nb; |
|
|
|
|
if (nb == 0) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ********************************************************** */ |
|
|
|
|
/* Return the correct result (take stats mode into account). */ |
|
|
|
|
|
|
|
|
|
if (stats) { |
|
|
|
|
UniValue res(UniValue::VOBJ); |
|
|
|
|
res.pushKV("blocks", chainActive.Height()); |
|
|
|
|
res.pushKV("count", static_cast<int>(count)); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return keys; |
|
|
|
|
} |
|
|
|
|