Verus Coin - this coin was backdoored by it's lead dev and should not be trusted!
https://git.hush.is/duke/backdoors/src/branch/master/vrsc.md
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2347 lines
93 KiB
2347 lines
93 KiB
// Copyright (c) 2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
|
|
|
#include <univalue.h>
|
|
#include "clientversion.h"
|
|
#include "init.h"
|
|
#include "key_io.h"
|
|
#include "main.h"
|
|
#include "net.h"
|
|
#include "netbase.h"
|
|
#include "rpc/server.h"
|
|
#include "timedata.h"
|
|
#include "txmempool.h"
|
|
#include "util.h"
|
|
#include "../version.h"
|
|
#include "pbaas/crosschainrpc.h"
|
|
#ifdef ENABLE_WALLET
|
|
#include "wallet/wallet.h"
|
|
#include "wallet/walletdb.h"
|
|
#endif
|
|
#include "tls/utiltls.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <boost/assign/list_of.hpp>
|
|
|
|
#include "zcash/Address.hpp"
|
|
#include "pbaas/pbaas.h"
|
|
#include <ostream>
|
|
#include <algorithm>
|
|
|
|
using namespace std;
|
|
|
|
/**
|
|
* @note Do not add or change anything in the information returned by this
|
|
* method. `getinfo` exists for backwards-compatibility only. It combines
|
|
* information from wildly different sources in the program, which is a mess,
|
|
* and is thus planned to be deprecated eventually.
|
|
*
|
|
* Based on the source of the information, new information should be added to:
|
|
* - `getblockchaininfo`,
|
|
* - `getnetworkinfo` or
|
|
* - `getwalletinfo`
|
|
*
|
|
* Or alternatively, create a specific query method for the information.
|
|
**/
|
|
|
|
extern void CopyNodeStats(std::vector<CNodeStats>& vstats);
|
|
int32_t Jumblr_depositaddradd(char *depositaddr);
|
|
int32_t Jumblr_secretaddradd(char *secretaddr);
|
|
uint64_t komodo_interestsum();
|
|
int32_t komodo_longestchain();
|
|
int32_t komodo_notarized_height(int32_t *prevhtp,uint256 *hashp,uint256 *txidp);
|
|
uint32_t komodo_chainactive_timestamp();
|
|
int32_t komodo_whoami(char *pubkeystr,int32_t height,uint32_t timestamp);
|
|
extern uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE;
|
|
extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN;
|
|
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
|
uint32_t komodo_segid32(char *coinaddr);
|
|
bool GetCoinSupply(int64_t &transparentSupply, int64_t *pzsupply, int64_t *pimmaturesupply, uint32_t height);
|
|
int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp);
|
|
|
|
extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT;
|
|
extern uint32_t ASSETCHAINS_CC;
|
|
extern uint32_t ASSETCHAINS_MAGIC;
|
|
extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY,ASSETCHAINS_ISSUANCE,ASSETCHAINS_LASTERA;
|
|
extern int32_t ASSETCHAINS_LWMAPOS;
|
|
extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[];
|
|
extern uint64_t ASSETCHAINS_ERAOPTIONS[];
|
|
|
|
UniValue getinfo(const UniValue& params, bool fHelp)
|
|
{
|
|
uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height,longestchain,kmdnotarized_height,txid_height;
|
|
if (fHelp || params.size() != 0)
|
|
throw runtime_error(
|
|
"getinfo\n"
|
|
"Returns an object containing various state info.\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"version\": xxxxx, (numeric) the server version\n"
|
|
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
|
|
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
|
|
" \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
|
|
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
|
|
" \"connections\": xxxxx, (numeric) the number of connections\n"
|
|
" \"tls_established\": xxxxx, (numeric) the number of TLS connections established\n"
|
|
" \"tls_verified\": xxxxx, (numeric) the number of TLS connection with validated certificates\n"
|
|
" \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n"
|
|
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
|
|
" \"testnet\": true|false, (boolean) if the server is using testnet or not\n"
|
|
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
|
|
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
|
|
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
|
|
" \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n"
|
|
" \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n"
|
|
" \"errors\": \"...\" (string) any error messages\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getinfo", "")
|
|
+ HelpExampleRpc("getinfo", "")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
proxyType proxy;
|
|
GetProxy(NET_IPV4, proxy);
|
|
notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid);
|
|
//fprintf(stderr,"after notarized_height %u\n",(uint32_t)time(NULL));
|
|
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("version", CLIENT_VERSION));
|
|
obj.push_back(Pair("protocolversion", PROTOCOL_VERSION));
|
|
obj.push_back(Pair("VRSCversion", VERUS_VERSION));
|
|
obj.push_back(Pair("notarized", notarized_height));
|
|
obj.push_back(Pair("prevMoMheight", prevMoMheight));
|
|
obj.push_back(Pair("notarizedhash", notarized_hash.ToString()));
|
|
obj.push_back(Pair("notarizedtxid", notarized_desttxid.ToString()));
|
|
txid_height = notarizedtxid_height(ASSETCHAINS_SYMBOL[0] != 0 ? (char *)"KMD" : (char *)"BTC",(char *)notarized_desttxid.ToString().c_str(),&kmdnotarized_height);
|
|
if ( txid_height > 0 )
|
|
obj.push_back(Pair("notarizedtxid_height", txid_height));
|
|
else obj.push_back(Pair("notarizedtxid_height", "mempool"));
|
|
if ( ASSETCHAINS_SYMBOL[0] != 0 )
|
|
obj.push_back(Pair("KMDnotarized_height", kmdnotarized_height));
|
|
obj.push_back(Pair("notarized_confirms", txid_height < kmdnotarized_height ? (kmdnotarized_height - txid_height + 1) : 0));
|
|
//fprintf(stderr,"after notarized_confirms %u\n",(uint32_t)time(NULL));
|
|
#ifdef ENABLE_WALLET
|
|
if (pwalletMain) {
|
|
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
|
|
}
|
|
#endif
|
|
//fprintf(stderr,"after wallet %u\n",(uint32_t)time(NULL));
|
|
obj.push_back(Pair("blocks", (int)chainActive.Height()));
|
|
if ( (longestchain = KOMODO_LONGESTCHAIN) == 0 || chainActive.Height() > longestchain )
|
|
longestchain = chainActive.Height();
|
|
//fprintf(stderr,"after longestchain %u\n",(uint32_t)time(NULL));
|
|
obj.push_back(Pair("longestchain", longestchain));
|
|
obj.push_back(Pair("timeoffset", GetTimeOffset()));
|
|
if ( chainActive.LastTip() != 0 )
|
|
obj.push_back(Pair("tiptime", (int)chainActive.LastTip()->nTime));
|
|
obj.push_back(Pair("connections", (int)vNodes.size()));
|
|
obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())));
|
|
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
|
|
obj.push_back(Pair("testnet", PBAAS_TESTMODE));
|
|
#ifdef ENABLE_WALLET
|
|
if (pwalletMain) {
|
|
LOCK(pwalletMain->cs_wallet);
|
|
obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime()));
|
|
obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
|
|
}
|
|
if (pwalletMain && pwalletMain->IsCrypted())
|
|
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
|
|
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
|
|
#endif
|
|
|
|
//Add TLS stats to getinfo
|
|
vector<CNodeStats> vstats;
|
|
CopyNodeStats(vstats);
|
|
int tlsEstablished = 0;
|
|
int tlsVerified = 0;
|
|
BOOST_FOREACH(const CNodeStats& stats, vstats) {
|
|
if (stats.fTLSEstablished)
|
|
tlsEstablished++;
|
|
|
|
if (stats.fTLSVerified)
|
|
tlsVerified++;
|
|
}
|
|
obj.push_back(Pair("tls_established", tlsEstablished));
|
|
obj.push_back(Pair("tls_verified", tlsVerified));
|
|
|
|
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
|
|
obj.push_back(Pair("errors", GetWarnings("statusbar")));
|
|
{
|
|
char pubkeystr[65]; int32_t notaryid;
|
|
if ( (notaryid= komodo_whoami(pubkeystr,(int32_t)chainActive.LastTip()->GetHeight(),komodo_chainactive_timestamp())) >= 0 )
|
|
{
|
|
obj.push_back(Pair("notaryid", notaryid));
|
|
obj.push_back(Pair("pubkey", pubkeystr));
|
|
if ( KOMODO_LASTMINED != 0 )
|
|
obj.push_back(Pair("lastmined", KOMODO_LASTMINED));
|
|
}
|
|
}
|
|
if ( ASSETCHAINS_CC != 0 )
|
|
obj.push_back(Pair("CCid", (int)ASSETCHAINS_CC));
|
|
obj.push_back(Pair("name", ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL));
|
|
if ( ASSETCHAINS_SYMBOL[0] != 0 )
|
|
{
|
|
//obj.push_back(Pair("name", ASSETCHAINS_SYMBOL));
|
|
obj.push_back(Pair("p2pport", ASSETCHAINS_P2PPORT));
|
|
obj.push_back(Pair("rpcport", ASSETCHAINS_RPCPORT));
|
|
obj.push_back(Pair("magic", (int)ASSETCHAINS_MAGIC));
|
|
|
|
obj.push_back(Pair("premine", ASSETCHAINS_SUPPLY));
|
|
|
|
if (ASSETCHAINS_ISSUANCE)
|
|
{
|
|
obj.push_back(Pair("issuance", ASSETCHAINS_ISSUANCE));
|
|
}
|
|
|
|
if ( ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_LASTERA > 0 )
|
|
{
|
|
std::string acReward = "", acHalving = "", acDecay = "", acEndSubsidy = "";
|
|
int lastEra = (int)ASSETCHAINS_LASTERA; // this is done to work around an ARM cross compiler
|
|
bool isFractional = false;
|
|
for (int i = 0; i <= lastEra; i++)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
acReward = std::to_string(ASSETCHAINS_REWARD[i]);
|
|
acHalving = std::to_string(ASSETCHAINS_HALVING[i]);
|
|
acDecay = std::to_string(ASSETCHAINS_DECAY[i]);
|
|
acEndSubsidy = std::to_string(ASSETCHAINS_ENDSUBSIDY[i]);
|
|
if (ASSETCHAINS_ERAOPTIONS[i] & CCurrencyDefinition::OPTION_FRACTIONAL)
|
|
{
|
|
//printf("%s: %s, ac_options: %s\n", __func__, std::to_string(ASSETCHAINS_ERAOPTIONS[i]).c_str(), GetArg("-ac_options","").c_str());
|
|
isFractional = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
acReward += "," + std::to_string(ASSETCHAINS_REWARD[i]);
|
|
acHalving += "," + std::to_string(ASSETCHAINS_HALVING[i]);
|
|
acDecay += "," + std::to_string(ASSETCHAINS_DECAY[i]);
|
|
acEndSubsidy += "," + std::to_string(ASSETCHAINS_ENDSUBSIDY[i]);
|
|
}
|
|
}
|
|
if (ASSETCHAINS_LASTERA > 0)
|
|
obj.push_back(Pair("eras", ASSETCHAINS_LASTERA + 1));
|
|
obj.push_back(Pair("reward", acReward));
|
|
obj.push_back(Pair("halving", acHalving));
|
|
obj.push_back(Pair("decay", acDecay));
|
|
obj.push_back(Pair("endsubsidy", acEndSubsidy));
|
|
if (isFractional)
|
|
{
|
|
obj.push_back(Pair("fractional", "true"));
|
|
obj.push_back(Pair("currencystate", ConnectedChains.GetCurrencyState((int)chainActive.Height()).ToUniValue()));
|
|
}
|
|
}
|
|
|
|
if ( ASSETCHAINS_COMMISSION != 0 )
|
|
obj.push_back(Pair("commission", ASSETCHAINS_COMMISSION));
|
|
if ( ASSETCHAINS_STAKED != 0 )
|
|
obj.push_back(Pair("staked", ASSETCHAINS_STAKED));
|
|
if ( ASSETCHAINS_LWMAPOS != 0 )
|
|
obj.push_back(Pair("veruspos", ASSETCHAINS_LWMAPOS));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
#ifdef ENABLE_WALLET
|
|
class DescribeAddressVisitor : public boost::static_visitor<UniValue>
|
|
{
|
|
public:
|
|
UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
|
|
|
|
UniValue operator()(const CKeyID &keyID) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
CPubKey vchPubKey;
|
|
obj.push_back(Pair("isscript", false));
|
|
if (pwalletMain && pwalletMain->GetPubKey(keyID, vchPubKey)) {
|
|
obj.push_back(Pair("pubkey", HexStr(vchPubKey))); // should return pubkeyhash, but not sure about compatibility impact
|
|
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const CPubKey &key) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("isscript", false));
|
|
if (pwalletMain && key.IsValid()) {
|
|
obj.push_back(Pair("pubkey", HexStr(key)));
|
|
obj.push_back(Pair("iscompressed", key.IsCompressed()));
|
|
}
|
|
else
|
|
{
|
|
obj.push_back(Pair("pubkey", "invalid"));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const CScriptID &scriptID) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
CScript subscript;
|
|
obj.push_back(Pair("isscript", true));
|
|
if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) {
|
|
std::vector<CTxDestination> addresses;
|
|
txnouttype whichType;
|
|
int nRequired;
|
|
ExtractDestinations(subscript, whichType, addresses, nRequired);
|
|
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
|
|
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
|
|
UniValue a(UniValue::VARR);
|
|
for (const CTxDestination& addr : addresses) {
|
|
a.push_back(EncodeDestination(addr));
|
|
}
|
|
obj.push_back(Pair("addresses", a));
|
|
if (whichType == TX_MULTISIG)
|
|
obj.push_back(Pair("sigsrequired", nRequired));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const CIdentityID &idID) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
CScript subscript;
|
|
obj.push_back(Pair("isscript", false));
|
|
obj.push_back(Pair("isidentity", true));
|
|
CIdentity id = CIdentity::LookupIdentity(idID);
|
|
if (id.IsValid()) {
|
|
if (id.IsRevoked())
|
|
{
|
|
obj.push_back(Pair("isrevoked", true));
|
|
}
|
|
else
|
|
{
|
|
obj.push_back(Pair("isrevoked", false));
|
|
UniValue a(UniValue::VARR);
|
|
for (const CTxDestination& addr : id.primaryAddresses) {
|
|
a.push_back(EncodeDestination(addr));
|
|
}
|
|
obj.push_back(Pair("addresses", a));
|
|
obj.push_back(Pair("sigsrequired", id.minSigs));
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const CQuantumID &qID) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
CScript subscript;
|
|
obj.push_back(Pair("isscript", false));
|
|
obj.push_back(Pair("isquantumkey", true));
|
|
obj.push_back(Pair("address", EncodeDestination(qID)));
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const CIndexID &idxID) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("isscript", false));
|
|
obj.push_back(Pair("isindexkey", true));
|
|
obj.push_back(Pair("address", EncodeDestination(idxID)));
|
|
return obj;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
UniValue coinsupply(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() > 1)
|
|
throw runtime_error("coinsupply <height>\n"
|
|
"\nReturn coin supply information at a given block height. If no height is given, the current height is used.\n"
|
|
"\nArguments:\n"
|
|
"1. \"height\" (integer, optional) Block height\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"result\" : \"success\", (string) If the request was successful.\n"
|
|
" \"coin\" : \"VRSC\", (string) The currency symbol of the native coin of this blockchain.\n"
|
|
" \"height\" : 420, (integer) The height of this coin supply data\n"
|
|
" \"supply\" : \"777.0\", (float) The transparent coin supply\n"
|
|
" \"zfunds\" : \"0.777\", (float) The shielded coin supply (in zaddrs)\n"
|
|
" \"total\" : \"777.777\", (float) The total coin supply, i.e. sum of supply + zfunds\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("coinsupply", "420")
|
|
+ HelpExampleRpc("coinsupply", "420")
|
|
);
|
|
|
|
uint32_t height = 0;
|
|
int64_t zfunds = 0, supply = 0, immature = 0;
|
|
UniValue result(UniValue::VOBJ);
|
|
|
|
if ( params.size() == 0 )
|
|
height = chainActive.Height();
|
|
else height = atoi(uni_get_str(params[0]));
|
|
|
|
if (height > 0 && height <= chainActive.Height()) {
|
|
if (GetCoinSupply(supply, &zfunds, &immature, height))
|
|
{
|
|
result.push_back(Pair("result", "success"));
|
|
result.push_back(Pair("coin", ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL));
|
|
result.push_back(Pair("height", (int)height));
|
|
result.push_back(Pair("supply", ValueFromAmount(supply)));
|
|
result.push_back(Pair("immature", ValueFromAmount(immature)));
|
|
result.push_back(Pair("zfunds", ValueFromAmount(zfunds)));
|
|
result.push_back(Pair("total", ValueFromAmount(zfunds + supply)));
|
|
} else result.push_back(Pair("error", "couldnt calculate supply"));
|
|
} else {
|
|
result.push_back(Pair("error", "invalid height"));
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
UniValue jumblr_deposit(const UniValue& params, bool fHelp)
|
|
{
|
|
int32_t retval; UniValue result(UniValue::VOBJ);
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error("jumblr_deposit \"depositaddress\"\n");
|
|
CBitcoinAddress address(params[0].get_str());
|
|
bool isValid = address.IsValid();
|
|
if ( isValid != 0 )
|
|
{
|
|
string addr = params[0].get_str();
|
|
if ( (retval= Jumblr_depositaddradd((char *)addr.c_str())) >= 0 )
|
|
{
|
|
result.push_back(Pair("result", retval));
|
|
JUMBLR_PAUSE = 0;
|
|
}
|
|
else result.push_back(Pair("error", retval));
|
|
} else result.push_back(Pair("error", "invalid address"));
|
|
return(result);
|
|
}
|
|
|
|
UniValue jumblr_secret(const UniValue& params, bool fHelp)
|
|
{
|
|
int32_t retval; UniValue result(UniValue::VOBJ);
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error("jumblr_secret \"secretaddress\"\n");
|
|
CBitcoinAddress address(params[0].get_str());
|
|
bool isValid = address.IsValid();
|
|
if ( isValid != 0 )
|
|
{
|
|
string addr = params[0].get_str();
|
|
retval = Jumblr_secretaddradd((char *)addr.c_str());
|
|
result.push_back(Pair("result", "success"));
|
|
result.push_back(Pair("num", retval));
|
|
JUMBLR_PAUSE = 0;
|
|
} else result.push_back(Pair("error", "invalid address"));
|
|
return(result);
|
|
}
|
|
|
|
UniValue jumblr_pause(const UniValue& params, bool fHelp)
|
|
{
|
|
int32_t retval; UniValue result(UniValue::VOBJ);
|
|
if (fHelp )
|
|
throw runtime_error("jumblr_pause\n");
|
|
JUMBLR_PAUSE = 1;
|
|
result.push_back(Pair("result", "paused"));
|
|
return(result);
|
|
}
|
|
|
|
UniValue jumblr_resume(const UniValue& params, bool fHelp)
|
|
{
|
|
int32_t retval; UniValue result(UniValue::VOBJ);
|
|
if (fHelp )
|
|
throw runtime_error("jumblr_resume\n");
|
|
JUMBLR_PAUSE = 0;
|
|
result.push_back(Pair("result", "resumed"));
|
|
return(result);
|
|
}
|
|
|
|
UniValue validateaddress(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"validateaddress \"komodoaddress\"\n"
|
|
"\nReturn information about the given Komodo address.\n"
|
|
"\nArguments:\n"
|
|
"1. \"komodoaddress\" (string, required) The Komodo address to validate\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
|
|
" \"address\" : \"komodoaddress\", (string) The Komodo address validated\n"
|
|
" \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
|
|
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
|
|
" \"isscript\" : true|false, (boolean) If the key is a script\n"
|
|
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
|
|
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
|
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("validateaddress", "\"RTZMZHDFSTFQst8XmX2dR4DaH87cEUs3gC\"")
|
|
+ HelpExampleRpc("validateaddress", "\"RTZMZHDFSTFQst8XmX2dR4DaH87cEUs3gC\"")
|
|
);
|
|
|
|
#ifdef ENABLE_WALLET
|
|
LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
|
|
#else
|
|
LOCK(cs_main);
|
|
#endif
|
|
|
|
CTxDestination dest = DecodeDestination(params[0].get_str());
|
|
bool isValid = IsValidDestination(dest);
|
|
|
|
UniValue ret(UniValue::VOBJ);
|
|
ret.push_back(Pair("isvalid", isValid));
|
|
if (isValid)
|
|
{
|
|
std::string currentAddress = EncodeDestination(dest);
|
|
ret.push_back(Pair("address", currentAddress));
|
|
|
|
CScript scriptPubKey = GetScriptForDestination(dest);
|
|
ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
|
ret.push_back(Pair("segid", (int32_t)komodo_segid32((char *)params[0].get_str().c_str()) & 0x3f));
|
|
#ifdef ENABLE_WALLET
|
|
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
|
|
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
|
|
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
|
|
UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
|
|
ret.pushKVs(detail);
|
|
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
|
|
ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name));
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
class DescribePaymentAddressVisitor : public boost::static_visitor<UniValue>
|
|
{
|
|
public:
|
|
UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); }
|
|
|
|
UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("type", "sprout"));
|
|
obj.push_back(Pair("payingkey", zaddr.a_pk.GetHex()));
|
|
obj.push_back(Pair("transmissionkey", zaddr.pk_enc.GetHex()));
|
|
#ifdef ENABLE_WALLET
|
|
if (pwalletMain) {
|
|
obj.push_back(Pair("ismine", pwalletMain->HaveSproutSpendingKey(zaddr)));
|
|
}
|
|
#endif
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const libzcash::SaplingPaymentAddress &zaddr) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("type", "sapling"));
|
|
obj.push_back(Pair("diversifier", HexStr(zaddr.d)));
|
|
obj.push_back(Pair("diversifiedtransmissionkey", zaddr.pk_d.GetHex()));
|
|
#ifdef ENABLE_WALLET
|
|
if (pwalletMain) {
|
|
libzcash::SaplingIncomingViewingKey ivk;
|
|
libzcash::SaplingExtendedFullViewingKey extfvk;
|
|
bool isMine = pwalletMain->GetSaplingIncomingViewingKey(zaddr, ivk) &&
|
|
pwalletMain->GetSaplingFullViewingKey(ivk, extfvk) &&
|
|
pwalletMain->HaveSaplingSpendingKey(extfvk);
|
|
obj.push_back(Pair("ismine", isMine));
|
|
}
|
|
#endif
|
|
return obj;
|
|
}
|
|
};
|
|
|
|
UniValue z_validateaddress(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"z_validateaddress \"zaddr\"\n"
|
|
"\nReturn information about the given z address.\n"
|
|
"\nArguments:\n"
|
|
"1. \"zaddr\" (string, required) The z address to validate\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
|
|
" \"address\" : \"zaddr\", (string) The z address validated\n"
|
|
" \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n"
|
|
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
|
|
" \"payingkey\" : \"hex\", (string) [sprout] The hex value of the paying key, a_pk\n"
|
|
" \"transmissionkey\" : \"hex\", (string) [sprout] The hex value of the transmission key, pk_enc\n"
|
|
" \"diversifier\" : \"hex\", (string) [sapling] The hex value of the diversifier, d\n"
|
|
" \"diversifiedtransmissionkey\" : \"hex\", (string) [sapling] The hex value of pk_d\n"
|
|
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("z_validateaddress", "\"zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL\"")
|
|
+ HelpExampleRpc("z_validateaddress", "\"zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL\"")
|
|
);
|
|
|
|
|
|
#ifdef ENABLE_WALLET
|
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
#else
|
|
LOCK(cs_main);
|
|
#endif
|
|
|
|
string strAddress = params[0].get_str();
|
|
auto address = DecodePaymentAddress(strAddress);
|
|
bool isValid = IsValidPaymentAddress(address);
|
|
|
|
UniValue ret(UniValue::VOBJ);
|
|
ret.push_back(Pair("isvalid", isValid));
|
|
if (isValid)
|
|
{
|
|
ret.push_back(Pair("address", strAddress));
|
|
UniValue detail = boost::apply_visitor(DescribePaymentAddressVisitor(), address);
|
|
ret.pushKVs(detail);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Used by addmultisigaddress / createmultisig:
|
|
*/
|
|
CScript _createmultisig_redeemScript(const UniValue& params)
|
|
{
|
|
int nRequired = params[0].get_int();
|
|
const UniValue& keys = params[1].get_array();
|
|
|
|
// Gather public keys
|
|
if (nRequired < 1)
|
|
throw runtime_error("a multisignature address must require at least one key to redeem");
|
|
if ((int)keys.size() < nRequired)
|
|
throw runtime_error(
|
|
strprintf("not enough keys supplied "
|
|
"(got %u keys, but need at least %d to redeem)", keys.size(), nRequired));
|
|
if (keys.size() > 16)
|
|
throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
|
|
std::vector<CPubKey> pubkeys;
|
|
pubkeys.resize(keys.size());
|
|
for (unsigned int i = 0; i < keys.size(); i++)
|
|
{
|
|
const std::string& ks = keys[i].get_str();
|
|
#ifdef ENABLE_WALLET
|
|
// Case 1: Bitcoin address and we have full public key:
|
|
CTxDestination dest = DecodeDestination(ks);
|
|
if (pwalletMain && IsValidDestination(dest)) {
|
|
const CKeyID *keyID = boost::get<CKeyID>(&dest);
|
|
if (!keyID) {
|
|
throw std::runtime_error(strprintf("%s does not refer to a key", ks));
|
|
}
|
|
CPubKey vchPubKey;
|
|
if (!pwalletMain->GetPubKey(*keyID, vchPubKey)) {
|
|
throw std::runtime_error(strprintf("no full public key for address %s", ks));
|
|
}
|
|
if (!vchPubKey.IsFullyValid())
|
|
throw runtime_error(" Invalid public key: "+ks);
|
|
pubkeys[i] = vchPubKey;
|
|
}
|
|
|
|
// Case 2: hex public key
|
|
else
|
|
#endif
|
|
if (IsHex(ks))
|
|
{
|
|
CPubKey vchPubKey(ParseHex(ks));
|
|
if (!vchPubKey.IsFullyValid())
|
|
throw runtime_error(" Invalid public key: "+ks);
|
|
pubkeys[i] = vchPubKey;
|
|
}
|
|
else
|
|
{
|
|
throw runtime_error(" Invalid public key: "+ks);
|
|
}
|
|
}
|
|
CScript result = GetScriptForMultisig(nRequired, pubkeys);
|
|
|
|
if (result.size() > CScript::MAX_SCRIPT_ELEMENT_SIZE)
|
|
throw runtime_error(
|
|
strprintf("redeemScript exceeds size limit: %d > %d", (int)result.size(), CScript::MAX_SCRIPT_ELEMENT_SIZE));
|
|
|
|
return result;
|
|
}
|
|
|
|
UniValue createmultisig(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 2 || params.size() > 2)
|
|
{
|
|
string msg = "createmultisig nrequired [\"key\",...]\n"
|
|
"\nCreates a multi-signature address with n signature of m keys required.\n"
|
|
"It returns a json object with the address and redeemScript.\n"
|
|
|
|
"\nArguments:\n"
|
|
"1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
|
|
"2. \"keys\" (string, required) A json array of keys which are Komodo addresses or hex-encoded public keys\n"
|
|
" [\n"
|
|
" \"key\" (string) Komodo address or hex-encoded public key\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n"
|
|
" \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n"
|
|
"}\n"
|
|
|
|
"\nExamples:\n"
|
|
"\nCreate a multisig address from 2 addresses\n"
|
|
+ HelpExampleCli("createmultisig", "2 \"[\\\"RTZMZHDFSTFQst8XmX2dR4DaH87cEUs3gC\\\",\\\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\\\"]\"") +
|
|
"\nAs a json rpc call\n"
|
|
+ HelpExampleRpc("createmultisig", "2, \"[\\\"RTZMZHDFSTFQst8XmX2dR4DaH87cEUs3gC\\\",\\\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\\\"]\"")
|
|
;
|
|
throw runtime_error(msg);
|
|
}
|
|
|
|
// Construct using pay-to-script-hash:
|
|
CScript inner = _createmultisig_redeemScript(params);
|
|
CScriptID innerID(inner);
|
|
|
|
UniValue result(UniValue::VOBJ);
|
|
result.push_back(Pair("address", EncodeDestination(innerID)));
|
|
result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
|
|
|
|
return result;
|
|
}
|
|
|
|
uint256 HashFile(std::string filepath)
|
|
{
|
|
CHashWriterSHA256 ss(SER_GETHASH, 0);
|
|
ifstream ifs = ifstream(filepath, std::ios::binary | std::ios::in);
|
|
if (ifs.is_open() && !ifs.eof())
|
|
{
|
|
std::vector<char> vch(4096);
|
|
int readNum = 0;
|
|
do
|
|
{
|
|
readNum = ifs.readsome(&vch[0], vch.size());
|
|
if (readNum)
|
|
{
|
|
ss.write(&vch[0], readNum);
|
|
}
|
|
} while (readNum != 0 && !ifs.eof());
|
|
|
|
ifs.close();
|
|
|
|
return ss.GetHash();
|
|
}
|
|
else
|
|
{
|
|
return uint256();
|
|
}
|
|
}
|
|
|
|
uint160 ParseVDXFIDInternal(const std::string &vdxfName)
|
|
{
|
|
uint160 vdxfID;
|
|
uint160 parentID;
|
|
|
|
if (vdxfName.empty())
|
|
{
|
|
return uint160();
|
|
}
|
|
|
|
// first, try to interpret the ID as an ID, in case it is
|
|
CTxDestination idDest = DecodeDestination(vdxfName);
|
|
|
|
if (idDest.which() == COptCCParams::ADDRTYPE_ID)
|
|
{
|
|
return GetDestinationID(idDest);
|
|
}
|
|
else if (vdxfName.back() != '@')
|
|
{
|
|
idDest = DecodeDestination(vdxfName + "@");
|
|
}
|
|
|
|
if (idDest.which() == COptCCParams::ADDRTYPE_ID)
|
|
{
|
|
vdxfID = GetDestinationID(idDest);
|
|
}
|
|
else
|
|
{
|
|
vdxfID = CVDXF::GetDataKey(vdxfName, parentID);
|
|
}
|
|
return vdxfID;
|
|
}
|
|
|
|
UniValue getvdxfid(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 1 || params.size() > 2)
|
|
throw runtime_error(
|
|
"getvdxfid \"vdxfuri\" '{\"vdxfkey\":\"i-address or vdxfkey\", \"uint256\":\"hexstr\", \"indexnum\":0}'\n"
|
|
"\nReturns the VDXF key of the URI string. For example \"vrsc::system.currency.export\"\n"
|
|
"\nArguments:\n"
|
|
" \"vdxfuri\" (string, required) This message is converted from hex, the data is hashed, then returned\n"
|
|
" \"{\"\n"
|
|
" \"vdxfkey\":\"i-address or vdxfkey\" (string, optional) VDXF key or i-address to combine via hash\n"
|
|
" \"uint256\":\"32bytehex\" (hexstr, optional) 256 bit hash to combine with hash\n"
|
|
" \"indexnum\":int (integer, optional) int32_t number to combine with hash\n"
|
|
" \"}\"\n"
|
|
"\nResult:\n"
|
|
"{ (object) object with both base58check and hex vdxfid values of string and parents\n"
|
|
" \"vdxfid\" (base58check) i-ID of the URI processed with the VDXF\n"
|
|
" \"hash160result\" (hexstring) 20 byte hash in hex of the URL string passed in, processed with the VDXF\n"
|
|
" \"qualifiedname\": (object) separate name and parent ID value\n"
|
|
" {\n"
|
|
" \"name\": \"namestr\" (string) leaf name\n"
|
|
" \"parentid\" | \"namespace\":\"string\" (string) parent ID (or namespace if VDXF key) of name\n"
|
|
" }\n"
|
|
" \"bounddata\": { (object) if additional data is bound to create the value, it is returned here"
|
|
" {\n"
|
|
" \"vdxfkey\":\"i-address or vdxfkey\" (string) i-address combined via hash\n"
|
|
" \"uint256\":\"32bytehex\" (hexstr) 256 bit hash combined with hash\n"
|
|
" \"indexnum\":int (integer) int32_t combined with hash\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("getvdxfid", "\"system.currency.export\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("getvdxfid", "\"idname::userdefinedgroup.subgroup.publishedname\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("getvdxfid", "\"idname::userdefinedgroup.subgroup.publishedname\"")
|
|
);
|
|
|
|
std::string vdxfName = uni_get_str(params[0]);
|
|
if (!vdxfName.size())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "No message to hash");
|
|
}
|
|
|
|
UniValue secondObj = (params.size() > 1) ? params[1] : UniValue(UniValue::VOBJ);
|
|
UniValue vdxfKeyInputUni = find_value(secondObj, "vdxfkey");
|
|
UniValue hashUniValue = find_value(secondObj, "uint256");
|
|
UniValue numUniValue = find_value(secondObj, "indexnum");
|
|
|
|
uint160 vdxfKeyInput;
|
|
uint256 hash256KeyKeyInput;
|
|
if (!vdxfKeyInputUni.isNull())
|
|
{
|
|
std::string vdxfKeyInputStr = uni_get_str(vdxfKeyInputUni);
|
|
vdxfKeyInput = ParseVDXFIDInternal(vdxfKeyInputStr);
|
|
if (vdxfKeyInput.IsNull())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid additional vdxf key to combine");
|
|
}
|
|
}
|
|
if (!hashUniValue.isNull())
|
|
{
|
|
hash256KeyKeyInput = uint256S(uni_get_str(hashUniValue));
|
|
if (hash256KeyKeyInput.IsNull())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid hash value to combine");
|
|
}
|
|
}
|
|
int32_t hashInputNum = uni_get_int(numUniValue);
|
|
|
|
uint160 vdxfID;
|
|
uint160 parentID;
|
|
std::string cleanName;
|
|
std::string parentIDName = "parentid";
|
|
|
|
// first, try to interpret the ID as an ID, in case it is
|
|
CTxDestination idDest = DecodeDestination(vdxfName);
|
|
|
|
if (idDest.which() == COptCCParams::ADDRTYPE_ID)
|
|
{
|
|
cleanName = CleanName(vdxfName, parentID, true, true);
|
|
vdxfID = GetDestinationID(idDest);
|
|
}
|
|
else
|
|
{
|
|
parentIDName = "namespace";
|
|
vdxfID = CVDXF::GetDataKey(vdxfName, parentID);
|
|
cleanName = vdxfName;
|
|
}
|
|
|
|
if (vdxfID.IsNull())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid ID or URI format");
|
|
}
|
|
|
|
// now, add optional values
|
|
UniValue boundData(UniValue::VOBJ);
|
|
if (!vdxfKeyInputUni.isNull())
|
|
{
|
|
if (hashUniValue.isNull())
|
|
{
|
|
vdxfID = CCrossChainRPCData::GetConditionID(vdxfID, vdxfKeyInput);
|
|
boundData.pushKV("vdxfkey", EncodeDestination(CIdentityID(vdxfKeyInput)));
|
|
if (!numUniValue.isNull())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify index without hash value");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (numUniValue.isNull())
|
|
{
|
|
vdxfID = CCrossChainRPCData::GetConditionID(vdxfID, vdxfKeyInput, hash256KeyKeyInput);
|
|
boundData.pushKV("vdxfkey", EncodeDestination(CIdentityID(vdxfKeyInput)));
|
|
boundData.pushKV("uint256", hash256KeyKeyInput.GetHex());
|
|
}
|
|
else
|
|
{
|
|
vdxfID = CCrossChainRPCData::GetConditionID(vdxfID, vdxfKeyInput, hash256KeyKeyInput, hashInputNum);
|
|
boundData.pushKV("vdxfkey", EncodeDestination(CIdentityID(vdxfKeyInput)));
|
|
boundData.pushKV("uint256", hash256KeyKeyInput.GetHex());
|
|
boundData.pushKV("indexnum", hashInputNum);
|
|
}
|
|
}
|
|
}
|
|
else if (!hashUniValue.isNull() && !numUniValue.isNull())
|
|
{
|
|
vdxfID = CCrossChainRPCData::GetConditionID(vdxfID, hash256KeyKeyInput, hashInputNum);
|
|
boundData.pushKV("uint256", hash256KeyKeyInput.GetHex());
|
|
boundData.pushKV("indexnum", hashInputNum);
|
|
}
|
|
else if (!hashUniValue.isNull() || !numUniValue.isNull())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify hash or numeric index without additional vdxf key or hash");
|
|
}
|
|
|
|
UniValue result(UniValue::VOBJ);
|
|
result.pushKV("vdxfid", EncodeDestination(CIdentityID(vdxfID)));
|
|
result.pushKV("hash160result", vdxfID.GetHex());
|
|
UniValue nameWithParent(UniValue::VOBJ);
|
|
nameWithParent.pushKV(parentIDName, EncodeDestination(CIdentityID(parentID)));
|
|
nameWithParent.pushKV("name", cleanName);
|
|
result.pushKV("qualifiedname", nameWithParent);
|
|
if (boundData.getKeys().size())
|
|
{
|
|
result.pushKV("bounddata", boundData);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
UniValue hashdata(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 1 || params.size() > 3)
|
|
throw runtime_error(
|
|
"hashdata \"hexdata\" \"hashtype\" \"personalstring\"\n"
|
|
"\nReturns the hash of the data in a hex message\n"
|
|
"\nArguments:\n"
|
|
" \"hexdata\" (string, required) This message is converted from hex, the data is hashed, then returned\n"
|
|
" \"hashtype\" (string, optional) one of (\"sha256rev\", \"sha256D\", \"blake2b\", \"blake2bnopersonal\", \"keccak256\", \"verushash2\", \"verushash2b\", \"verushash2.1\"), defaults to sha256\n"
|
|
" \"personalstring\" (string, optional) For hashes with personalization string, such as blake2b, this is optional and will default to daemon default if not specified\n"
|
|
"\nResult:\n"
|
|
" \"hashresult\" (hexstring) 32 byte hash in hex of the data passed in using the hash of the specific blockheight\n"
|
|
"\nExamples:\n"
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("signmessage", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"my message\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("verifymessage", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"signature\" \"my message\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("verifymessage", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\", \"signature\", \"my message\"")
|
|
);
|
|
|
|
std::string hexMessage = uni_get_str(params[0]);
|
|
if (!hexMessage.size())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "No message to hash");
|
|
}
|
|
|
|
std::vector<unsigned char> vmsg;
|
|
try
|
|
{
|
|
vmsg = ParseHex(hexMessage);
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Message to hash must be in hexadecimal format");
|
|
}
|
|
|
|
std::string hashType = params.size() > 1 ? uni_get_str(params[1]) : "sha256";
|
|
|
|
uint256 result;
|
|
|
|
if (hashType == "sha256")
|
|
{
|
|
CHashWriterSHA256 hw(SER_GETHASH, PROTOCOL_VERSION);
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
// to be compatible with data and file hashing tools when users compare the output, such as sha256sum,
|
|
// we reverse the normally little endian value
|
|
std::reverse(result.begin(), result.end());
|
|
}
|
|
else if (hashType == "sha256D")
|
|
{
|
|
CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION);
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
else if (hashType == "blake2b")
|
|
{
|
|
std::string personalString;
|
|
if (params.size() > 2)
|
|
{
|
|
personalString = uni_get_str(params[2]);
|
|
std::vector<unsigned char> personalVec(personalString[0], personalString[0] + personalString.size());
|
|
personalVec.resize(crypto_generichash_blake2b_PERSONALBYTES);
|
|
CBLAKE2bWriter hw(SER_GETHASH, PROTOCOL_VERSION, &(personalVec[0]));
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
else
|
|
{
|
|
CBLAKE2bWriter hw(SER_GETHASH, PROTOCOL_VERSION);
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
}
|
|
else if (hashType == "blake2bnopersonal")
|
|
{
|
|
CBLAKE2bWriter hw(SER_GETHASH, PROTOCOL_VERSION, nullptr);
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
else if (hashType == "keccak256")
|
|
{
|
|
CKeccack256Writer hw;
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
else if (hashType == "verushash2")
|
|
{
|
|
CVerusHashV2Writer hw(SER_GETHASH, PROTOCOL_VERSION);
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
else if (hashType == "verushash2b")
|
|
{
|
|
CVerusHashV2bWriter hw(SER_GETHASH, PROTOCOL_VERSION, CActivationHeight::ACTIVATE_VERUSHASH2);
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
else if (hashType == "verushash2.1")
|
|
{
|
|
CVerusHashV2bWriter hw(SER_GETHASH, PROTOCOL_VERSION, CActivationHeight::ACTIVATE_VERUSHASH2_1);
|
|
hw.write((const char *)vmsg.data(), vmsg.size());
|
|
result = hw.GetHash();
|
|
}
|
|
else
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Hash type " + hashType + " must be one of (\"sha256rev\", \"sha256D\", \"blake2b\", \"blake2bnopersonal\", \"keccak256\", \"verushash2\", \"verushash2b\", \"verushash2.1\")");
|
|
}
|
|
return result.GetHex();
|
|
}
|
|
|
|
UniValue verifyhash(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 3 || params.size() > 4)
|
|
throw runtime_error(
|
|
"verifyhash \"address or identity\" \"signature\" \"hexhash\" \"checklatest\"\n"
|
|
"\nVerify a signed message\n"
|
|
"\nArguments:\n"
|
|
"1. \"t-addr or identity\" (string, required) The transparent address or identity that signed the data.\n"
|
|
"2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage/signfile).\n"
|
|
"3. \"hexhash\" (string, required) Hash of the message or file that was signed.\n"
|
|
"3. \"checklatest\" (bool, optional) If true, checks signature validity based on latest identity. defaults to false,\n"
|
|
" which determines validity of signing height stored in signature.\n"
|
|
"\nResult:\n"
|
|
"true|false (boolean) If the signature is verified or not.\n"
|
|
"\nExamples:\n"
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("signfile", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"filepath/filename\"") +
|
|
"or\n"
|
|
+ HelpExampleCli("signmessage", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"my message\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("verifyhash", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"signature\" \"hexhash\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("verifyhash", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\", \"signature\", \"hexhash\"")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
string strAddress = params[0].get_str();
|
|
string strSign = params[1].get_str();
|
|
string strHash = params[2].get_str();
|
|
|
|
bool fInvalid = false;
|
|
uint256 msgHash;
|
|
|
|
CTxDestination destination = DecodeDestination(strAddress);
|
|
if (!IsValidDestination(destination)) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
|
|
}
|
|
|
|
if (!strHash.size())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "No hash to verify");
|
|
}
|
|
|
|
try
|
|
{
|
|
msgHash = uint256S(strHash.c_str());
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "hexhash must be a valid hexadecimal hash value");
|
|
}
|
|
|
|
// we expect a hash to be passed in reversed, for compatibility with file and data hashing tools like sha256sum
|
|
std::reverse(msgHash.begin(), msgHash.end());
|
|
|
|
if (destination.which() == COptCCParams::ADDRTYPE_ID)
|
|
{
|
|
// lookup identity from the requested blockheight
|
|
bool checkLatest = params.size() > 3 && uni_get_bool(params[3]);
|
|
|
|
CIdentitySignature signature;
|
|
|
|
// get the signature, a hex string, which is deserialized into an instance of the ID signature class
|
|
std::vector<unsigned char> sigVec;
|
|
try
|
|
{
|
|
sigVec = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
if (fInvalid)
|
|
{
|
|
sigVec.clear();
|
|
}
|
|
|
|
if (sigVec.size())
|
|
{
|
|
signature = CIdentitySignature(sigVec);
|
|
}
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
std::cerr << e.what() << '\n';
|
|
}
|
|
|
|
if (signature.signatures.size())
|
|
{
|
|
CHashWriterSHA256 ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << verusDataSignaturePrefix;
|
|
ss << ConnectedChains.ThisChain().GetID();
|
|
ss << signature.blockHeight;
|
|
ss << GetDestinationID(destination);
|
|
ss << msgHash;
|
|
|
|
msgHash = ss.GetHash();
|
|
|
|
std::set<uint160> signatureKeyIDs;
|
|
for (auto &oneSig : signature.signatures)
|
|
{
|
|
CPubKey pubkey;
|
|
if (pubkey.RecoverCompact(msgHash, oneSig))
|
|
{
|
|
signatureKeyIDs.insert(pubkey.GetID());
|
|
}
|
|
}
|
|
|
|
CIdentity identity;
|
|
int numSigs = 0;
|
|
if (signatureKeyIDs.size() != 0)
|
|
{
|
|
identity = CIdentity::LookupIdentity(GetDestinationID(destination), checkLatest ? 0 : signature.blockHeight);
|
|
if (identity.IsValidUnrevoked())
|
|
{
|
|
// remove all valid addresses and count
|
|
for (auto &oneAddr : identity.primaryAddresses)
|
|
{
|
|
if (!(oneAddr.which() == COptCCParams::ADDRTYPE_PK || oneAddr.which() == COptCCParams::ADDRTYPE_PKH))
|
|
{
|
|
numSigs = 0;
|
|
break;
|
|
}
|
|
uint160 addrID = GetDestinationID(oneAddr);
|
|
if (signatureKeyIDs.count(addrID))
|
|
{
|
|
numSigs++;
|
|
signatureKeyIDs.erase(addrID);
|
|
if (!signatureKeyIDs.size())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// all signatures must be from valid keys, and if there are enough, it is valid
|
|
return signatureKeyIDs.size() == 0 && numSigs >= identity.minSigs;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
const CKeyID *keyID = boost::get<CKeyID>(&destination);
|
|
if (!keyID) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
|
|
}
|
|
|
|
vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
|
|
if (fInvalid)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
|
|
|
|
CHashWriterSHA256 ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << verusDataSignaturePrefix;
|
|
ss << msgHash;
|
|
|
|
CPubKey pubkey;
|
|
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
|
|
return false;
|
|
|
|
return (pubkey.GetID() == *keyID);
|
|
}
|
|
}
|
|
|
|
UniValue verifymessage(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 3 || params.size() > 4)
|
|
throw runtime_error(
|
|
"verifymessage \"address or identity\" \"signature\" \"message\" \"checklatest\"\n"
|
|
"\nVerify a signed message\n"
|
|
"\nArguments:\n"
|
|
"1. \"t-addr or identity\" (string, required) The transparent address or identity that signed the message.\n"
|
|
"2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n"
|
|
"3. \"message\" (string, required) The message that was signed.\n"
|
|
"3. \"checklatest\" (bool, optional) If true, checks signature validity based on latest identity. defaults to false,\n"
|
|
" which determines validity of signing height stored in signature.\n"
|
|
"\nResult:\n"
|
|
"true|false (boolean) If the signature is verified or not.\n"
|
|
"\nExamples:\n"
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("signmessage", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"my message\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("verifymessage", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"signature\" \"my message\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("verifymessage", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\", \"signature\", \"my message\"")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
string strAddress = params[0].get_str();
|
|
string strSign = params[1].get_str();
|
|
string strMessage = params[2].get_str();
|
|
bool fInvalid = false;
|
|
|
|
CTxDestination destination = DecodeDestination(strAddress);
|
|
if (!IsValidDestination(destination)) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
|
|
}
|
|
|
|
if (destination.which() == COptCCParams::ADDRTYPE_ID)
|
|
{
|
|
// lookup identity from the requested blockheight
|
|
bool checkLatest = params.size() > 3 && uni_get_bool(params[3]);
|
|
|
|
CIdentitySignature signature;
|
|
|
|
// get the signature, a hex string, which is deserialized into an instance of the ID signature class
|
|
std::vector<unsigned char> sigVec;
|
|
try
|
|
{
|
|
sigVec = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
if (fInvalid)
|
|
{
|
|
sigVec.clear();
|
|
}
|
|
|
|
if (sigVec.size())
|
|
{
|
|
signature = CIdentitySignature(sigVec);
|
|
}
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
std::cerr << e.what() << '\n';
|
|
}
|
|
|
|
if (signature.signatures.size())
|
|
{
|
|
CHashWriterSHA256 ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << strMessage;
|
|
uint256 msgHash = ss.GetHash();
|
|
|
|
ss.Reset();
|
|
ss << verusDataSignaturePrefix;
|
|
ss << ConnectedChains.ThisChain().GetID();
|
|
ss << signature.blockHeight;
|
|
ss << GetDestinationID(destination);
|
|
ss << msgHash;
|
|
|
|
msgHash = ss.GetHash();
|
|
|
|
std::set<uint160> signatureKeyIDs;
|
|
for (auto &oneSig : signature.signatures)
|
|
{
|
|
CPubKey pubkey;
|
|
if (pubkey.RecoverCompact(msgHash, oneSig))
|
|
{
|
|
signatureKeyIDs.insert(pubkey.GetID());
|
|
}
|
|
}
|
|
|
|
CIdentity identity;
|
|
int numSigs = 0;
|
|
if (signatureKeyIDs.size() != 0)
|
|
{
|
|
identity = CIdentity::LookupIdentity(GetDestinationID(destination), checkLatest ? 0 : signature.blockHeight);
|
|
if (identity.IsValidUnrevoked())
|
|
{
|
|
// remove all valid addresses and count
|
|
for (auto &oneAddr : identity.primaryAddresses)
|
|
{
|
|
if (!(oneAddr.which() == COptCCParams::ADDRTYPE_PK || oneAddr.which() == COptCCParams::ADDRTYPE_PKH))
|
|
{
|
|
numSigs = 0;
|
|
break;
|
|
}
|
|
uint160 addrID = GetDestinationID(oneAddr);
|
|
if (signatureKeyIDs.count(addrID))
|
|
{
|
|
numSigs++;
|
|
signatureKeyIDs.erase(addrID);
|
|
if (!signatureKeyIDs.size())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// all signatures must be from valid keys, and if there are enough, it is valid
|
|
return signatureKeyIDs.size() == 0 && numSigs >= identity.minSigs;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
const CKeyID *keyID = boost::get<CKeyID>(&destination);
|
|
if (!keyID) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
|
|
}
|
|
|
|
vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
|
|
if (fInvalid)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
|
|
|
|
CHashWriterSHA256 ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << strMessage;
|
|
uint256 msgHash = ss.GetHash();
|
|
ss.Reset();
|
|
ss << verusDataSignaturePrefix;
|
|
ss << msgHash;
|
|
|
|
CPubKey pubkey;
|
|
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
|
|
return false;
|
|
|
|
return (pubkey.GetID() == *keyID);
|
|
}
|
|
}
|
|
|
|
UniValue verifyfile(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 3 || params.size() > 4)
|
|
throw runtime_error(
|
|
"verifyfile \"address or identity\" \"signature\" \"filepath/filename\" \"checklatest\"\n"
|
|
"\nVerify a signed file\n"
|
|
"\nArguments:\n"
|
|
"1. \"t-addr or identity\" (string, required) The transparent address or identity that signed the file.\n"
|
|
"2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signfile).\n"
|
|
"3. \"filename\" (string, required) The file, which must be available locally to the daemon and that was signed.\n"
|
|
"3. \"checklatest\" (bool, optional) If true, checks signature validity based on latest identity. defaults to false,\n"
|
|
" which determines validity of signing height stored in signature.\n"
|
|
"\nResult:\n"
|
|
"true|false (boolean) If the signature is verified or not.\n"
|
|
"\nExamples:\n"
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("signfile", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"filepath/filename\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("verifyfile", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\" \"signature\" \"filepath/filename\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("verifyfile", "\"RNKiEBduBru6Siv1cZRVhp4fkZNyPska6z\", \"signature\", \"filepath/filename\"")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
string strAddress = params[0].get_str();
|
|
string strSign = params[1].get_str();
|
|
string strFileName = params[2].get_str();
|
|
bool fInvalid = false;
|
|
|
|
CTxDestination destination = DecodeDestination(strAddress);
|
|
if (!IsValidDestination(destination)) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
|
|
}
|
|
|
|
if (destination.which() == COptCCParams::ADDRTYPE_ID)
|
|
{
|
|
// lookup identity from the requested blockheight
|
|
bool checkLatest = params.size() == 4 && uni_get_bool(params[3]);
|
|
|
|
CIdentitySignature signature;
|
|
|
|
// get the signature, a hex string, which is deserialized into an instance of the ID signature class
|
|
std::vector<unsigned char> sigVec;
|
|
try
|
|
{
|
|
sigVec = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
if (fInvalid)
|
|
{
|
|
sigVec.clear();
|
|
}
|
|
|
|
if (sigVec.size())
|
|
{
|
|
signature = CIdentitySignature(sigVec);
|
|
}
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
LogPrintf("Exception decoding file signature %s\n", e.what());
|
|
signature = CIdentitySignature();
|
|
}
|
|
|
|
if (signature.signatures.size())
|
|
{
|
|
uint256 msgHash = HashFile(strFileName);
|
|
|
|
if (msgHash.IsNull())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot open file " + strFileName);
|
|
}
|
|
else
|
|
{
|
|
CHashWriterSHA256 ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << verusDataSignaturePrefix;
|
|
ss << ConnectedChains.ThisChain().GetID();
|
|
ss << signature.blockHeight;
|
|
ss << GetDestinationID(destination);
|
|
ss << msgHash;
|
|
msgHash = ss.GetHash();
|
|
}
|
|
|
|
std::set<uint160> signatureKeyIDs;
|
|
for (auto &oneSig : signature.signatures)
|
|
{
|
|
CPubKey pubkey;
|
|
if (pubkey.RecoverCompact(msgHash, oneSig))
|
|
{
|
|
signatureKeyIDs.insert(pubkey.GetID());
|
|
}
|
|
}
|
|
|
|
CIdentity identity;
|
|
int numSigs = 0;
|
|
if (signatureKeyIDs.size() != 0)
|
|
{
|
|
identity = CIdentity::LookupIdentity(GetDestinationID(destination), checkLatest ? 0 : signature.blockHeight);
|
|
if (identity.IsValidUnrevoked())
|
|
{
|
|
// remove all valid addresses and count
|
|
for (auto &oneAddr : identity.primaryAddresses)
|
|
{
|
|
if (!(oneAddr.which() == COptCCParams::ADDRTYPE_PK || oneAddr.which() == COptCCParams::ADDRTYPE_PKH))
|
|
{
|
|
numSigs = 0;
|
|
break;
|
|
}
|
|
uint160 addrID = GetDestinationID(oneAddr);
|
|
if (signatureKeyIDs.count(addrID))
|
|
{
|
|
numSigs++;
|
|
signatureKeyIDs.erase(addrID);
|
|
if (!signatureKeyIDs.size())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// all signatures must be from valid keys, and if there are enough, it is valid
|
|
return signatureKeyIDs.size() == 0 && numSigs >= identity.minSigs;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
const CKeyID *keyID = boost::get<CKeyID>(&destination);
|
|
if (!keyID) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
|
|
}
|
|
|
|
vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
|
|
if (fInvalid)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
|
|
|
|
uint256 msgHash = HashFile(strFileName);
|
|
|
|
if (msgHash.IsNull())
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot open file " + strFileName);
|
|
}
|
|
else
|
|
{
|
|
CHashWriterSHA256 ss(SER_GETHASH, 0);
|
|
ss << verusDataSignaturePrefix;
|
|
ss << msgHash;
|
|
msgHash = ss.GetHash();
|
|
}
|
|
|
|
CPubKey pubkey;
|
|
if (!pubkey.RecoverCompact(msgHash, vchSig))
|
|
return false;
|
|
|
|
return (pubkey.GetID() == *keyID);
|
|
}
|
|
}
|
|
|
|
UniValue setmocktime(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"setmocktime timestamp\n"
|
|
"\nSet the local time to given timestamp (-regtest only)\n"
|
|
"\nArguments:\n"
|
|
"1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n"
|
|
" Pass 0 to go back to using the system time."
|
|
);
|
|
|
|
if (!Params().MineBlocksOnDemand())
|
|
throw runtime_error("setmocktime for regression testing (-regtest mode) only");
|
|
|
|
// cs_vNodes is locked and node send/receive times are updated
|
|
// atomically with the time change to prevent peers from being
|
|
// disconnected because we think we haven't communicated with them
|
|
// in a long time.
|
|
LOCK2(cs_main, cs_vNodes);
|
|
|
|
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
|
|
SetMockTime(params[0].get_int64());
|
|
|
|
uint64_t t = GetTime();
|
|
BOOST_FOREACH(CNode* pnode, vNodes) {
|
|
pnode->nLastSend = pnode->nLastRecv = t;
|
|
}
|
|
|
|
return NullUniValue;
|
|
}
|
|
|
|
bool getAddressFromIndex(
|
|
const int &type, const uint160 &hash, std::string &address)
|
|
{
|
|
if (type == CScript::P2ID) {
|
|
address = EncodeDestination(CIdentityID(hash));
|
|
} else if (type == CScript::P2SH) {
|
|
address = EncodeDestination(CScriptID(hash));
|
|
} else if (type == CScript::P2PKH) {
|
|
address = EncodeDestination(CKeyID(hash));
|
|
} else if (type == CScript::P2IDX) {
|
|
address = EncodeDestination(CIndexID(hash));
|
|
} else if (type == CScript::P2QRK) {
|
|
address = EncodeDestination(CQuantumID(hash));
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool getAddressesFromParams(const UniValue& params, std::vector<std::pair<uint160, int> > &addresses)
|
|
{
|
|
if (params[0].isStr()) {
|
|
CBitcoinAddress address(params[0].get_str());
|
|
uint160 hashBytes;
|
|
int type = 0;
|
|
if (!address.GetIndexKey(hashBytes, type)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
addresses.push_back(std::make_pair(hashBytes, type));
|
|
} else if (params[0].isObject()) {
|
|
|
|
UniValue addressValues = find_value(params[0].get_obj(), "addresses");
|
|
if (!addressValues.isArray()) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Addresses is expected to be an array");
|
|
}
|
|
|
|
std::vector<UniValue> values = addressValues.getValues();
|
|
|
|
for (std::vector<UniValue>::iterator it = values.begin(); it != values.end(); ++it) {
|
|
|
|
CBitcoinAddress address(it->get_str());
|
|
uint160 hashBytes;
|
|
int type = 0;
|
|
if (!address.GetIndexKey(hashBytes, type)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid addresses");
|
|
}
|
|
addresses.push_back(std::make_pair(hashBytes, type));
|
|
}
|
|
} else {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid addresse");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool heightSort(std::pair<CAddressUnspentKey, CAddressUnspentValue> a,
|
|
std::pair<CAddressUnspentKey, CAddressUnspentValue> b) {
|
|
return a.second.blockHeight < b.second.blockHeight;
|
|
}
|
|
|
|
bool timestampSort(std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> a,
|
|
std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> b) {
|
|
return a.second.time < b.second.time;
|
|
}
|
|
|
|
|
|
void CurrencyValuesAndNames(UniValue &output, bool spending, const CScript &script, CAmount satoshis, bool friendlyNames=false);
|
|
void CurrencyValuesAndNames(UniValue &output, bool spending, const CScript &script, CAmount satoshis, bool friendlyNames)
|
|
{
|
|
if (CConstVerusSolutionVector::GetVersionByHeight(chainActive.Height()) >= CActivationHeight::ACTIVATE_PBAAS)
|
|
{
|
|
CCurrencyValueMap reserves = script.ReserveOutValue();
|
|
if (spending)
|
|
{
|
|
reserves = reserves * -1;
|
|
}
|
|
if (satoshis)
|
|
{
|
|
reserves.valueMap[ASSETCHAINS_CHAINID] = satoshis;
|
|
}
|
|
if (reserves.valueMap.size())
|
|
{
|
|
UniValue currencyBal(UniValue::VOBJ);
|
|
UniValue currencyNames(UniValue::VOBJ);
|
|
for (auto &oneBalance : reserves.valueMap)
|
|
{
|
|
std::string name = EncodeDestination(CIdentityID(oneBalance.first));
|
|
currencyBal.push_back(make_pair(name, ValueFromAmount(oneBalance.second)));
|
|
if (friendlyNames)
|
|
{
|
|
currencyNames.pushKV(name, ConnectedChains.GetFriendlyCurrencyName(oneBalance.first));
|
|
}
|
|
}
|
|
output.pushKV("currencyvalues", currencyBal);
|
|
if (friendlyNames)
|
|
{
|
|
output.pushKV("currencynames", currencyNames);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CurrencyValuesAndNames(UniValue &output, bool spending, const CTransaction &tx, int index, CAmount satoshis, bool friendlyNames=false);
|
|
void CurrencyValuesAndNames(UniValue &output, bool spending, const CTransaction &tx, int index, CAmount satoshis, bool friendlyNames)
|
|
{
|
|
CScript script;
|
|
if (spending) {
|
|
CTransaction priorOutTx;
|
|
uint256 blockHash;
|
|
if (tx.vin.size() > index && index >= 0 && myGetTransaction(tx.vin[index].prevout.hash, priorOutTx, blockHash))
|
|
{
|
|
script = priorOutTx.vout[tx.vin[index].prevout.n].scriptPubKey;
|
|
}
|
|
else
|
|
{
|
|
throw JSONRPCError(RPC_DATABASE_ERROR, "Unable to retrieve data to retrieve spending currency values");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tx.vout.size() > index && index >= 0)
|
|
{
|
|
script = tx.vout[index].scriptPubKey;
|
|
}
|
|
else
|
|
{
|
|
throw JSONRPCError(RPC_DATABASE_ERROR, "Unable to retrieve data to for currency output values");
|
|
}
|
|
}
|
|
return CurrencyValuesAndNames(output, spending, script, satoshis, friendlyNames);
|
|
}
|
|
|
|
UniValue AddressMemPoolUni(const std::vector<std::pair<uint160, int>> &addresses, bool friendlyNames)
|
|
{
|
|
CTransaction curTx;
|
|
|
|
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > indexes;
|
|
|
|
if (!mempool.getAddressIndex(addresses, indexes)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
|
}
|
|
|
|
std::sort(indexes.begin(), indexes.end(), timestampSort);
|
|
|
|
UniValue result(UniValue::VARR);
|
|
|
|
for (std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> >::iterator it = indexes.begin();
|
|
it != indexes.end(); it++) {
|
|
|
|
std::string address;
|
|
if (!getAddressFromIndex(it->first.type, it->first.addressBytes, address)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
|
|
}
|
|
|
|
UniValue delta(UniValue::VOBJ);
|
|
delta.push_back(Pair("address", address));
|
|
delta.push_back(Pair("txid", it->first.txhash.GetHex()));
|
|
delta.push_back(Pair("index", (int)it->first.index));
|
|
delta.push_back(Pair("satoshis", it->second.amount));
|
|
delta.push_back(Pair("spending", (bool)it->first.spending));
|
|
if (!it->first.txhash.IsNull() && it->first.txhash == curTx.GetHash() || mempool.lookup(it->first.txhash, curTx))
|
|
{
|
|
CurrencyValuesAndNames(delta, it->first.spending, curTx, it->first.index, it->second.amount, friendlyNames);
|
|
}
|
|
delta.push_back(Pair("timestamp", it->second.time));
|
|
if (it->second.amount < 0) {
|
|
delta.push_back(Pair("prevtxid", it->second.prevhash.GetHex()));
|
|
delta.push_back(Pair("prevout", (int)it->second.prevout));
|
|
}
|
|
result.push_back(delta);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
UniValue getaddressmempool(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"getaddressmempool\n"
|
|
"\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n"
|
|
"\nArguments:\n"
|
|
"{\n"
|
|
" \"addresses\"\n"
|
|
" [\n"
|
|
" \"address\" (string) The base58check encoded address\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
" \"friendlynames\" (boolean) Include additional array of friendly names keyed by currency i-addresses\n"
|
|
"}\n"
|
|
"\nResult:\n"
|
|
"[\n"
|
|
" {\n"
|
|
" \"address\" (string) The base58check encoded address\n"
|
|
" \"txid\" (string) The related txid\n"
|
|
" \"index\" (number) The related input or output index\n"
|
|
" \"satoshis\" (number) The difference of satoshis\n"
|
|
" \"timestamp\" (number) The time the transaction entered the mempool (seconds)\n"
|
|
" \"prevtxid\" (string) The previous txid (if spending)\n"
|
|
" \"prevout\" (string) The previous transaction output index (if spending)\n"
|
|
" }\n"
|
|
"]\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'")
|
|
+ HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}")
|
|
);
|
|
|
|
std::vector<std::pair<uint160, int> > addresses;
|
|
UniValue result(UniValue::VARR);
|
|
|
|
if (!getAddressesFromParams(params, addresses)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
|
|
if (uni_get_bool(find_value(params[0].get_obj(), "friendlynames")))
|
|
{
|
|
LOCK2(cs_main, mempool.cs);
|
|
result = AddressMemPoolUni(addresses, true);
|
|
}
|
|
else
|
|
{
|
|
LOCK(mempool.cs);
|
|
result = AddressMemPoolUni(addresses, false);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
UniValue getaddressutxos(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"getaddressutxos\n"
|
|
"\nReturns all unspent outputs for an address (requires addressindex to be enabled).\n"
|
|
"\nArguments:\n"
|
|
"{\n"
|
|
" \"addresses\"\n"
|
|
" [\n"
|
|
" \"address\" (string) The base58check encoded address\n"
|
|
" ,...\n"
|
|
" ],\n"
|
|
" \"chaininfo\" (boolean) Include chain info with results\n"
|
|
" \"friendlynames\" (boolean) Include additional array of friendly names keyed by currency i-addresses\n"
|
|
"}\n"
|
|
"\nResult\n"
|
|
"[\n"
|
|
" {\n"
|
|
" \"address\" (string) The address base58check encoded\n"
|
|
" \"txid\" (string) The output txid\n"
|
|
" \"height\" (number) The block height\n"
|
|
" \"outputIndex\" (number) The output index\n"
|
|
" \"script\" (strin) The script hex encoded\n"
|
|
" \"satoshis\" (number) The number of satoshis of the output\n"
|
|
" }\n"
|
|
"]\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'")
|
|
+ HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}")
|
|
);
|
|
|
|
bool includeChainInfo = uni_get_bool(find_value(params[0].get_obj(), "chaininfo"));
|
|
bool friendlyNames = uni_get_bool(find_value(params[0].get_obj(), "friendlynames"));
|
|
|
|
std::vector<std::pair<uint160, int> > addresses;
|
|
|
|
if (!getAddressesFromParams(params, addresses)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
|
|
LOCK(cs_main);
|
|
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
|
|
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
|
if (!GetAddressUnspent((*it).first, (*it).second, unspentOutputs)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
|
}
|
|
}
|
|
|
|
std::sort(unspentOutputs.begin(), unspentOutputs.end(), heightSort);
|
|
|
|
UniValue utxos(UniValue::VARR);
|
|
|
|
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) {
|
|
UniValue output(UniValue::VOBJ);
|
|
|
|
std::string address = "";
|
|
|
|
COptCCParams p;
|
|
if (it->second.script.IsPayToCryptoCondition(p) && p.IsValid())
|
|
{
|
|
txnouttype outType;
|
|
std::vector<CTxDestination> addresses;
|
|
int required;
|
|
if (ExtractDestinations(it->second.script, outType, addresses, required))
|
|
{
|
|
UniValue addressesUni(UniValue::VARR);
|
|
for (auto addr : addresses)
|
|
{
|
|
addressesUni.push_back(EncodeDestination(addr));
|
|
if (GetDestinationID(addr) == it->first.hashBytes)
|
|
{
|
|
address = EncodeDestination(addr);
|
|
}
|
|
}
|
|
if (addressesUni.size() > 1)
|
|
{
|
|
output.push_back(Pair("addresses", addressesUni));
|
|
}
|
|
}
|
|
}
|
|
if (address == "" && !getAddressFromIndex(it->first.type, it->first.hashBytes, address))
|
|
{
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
|
|
}
|
|
|
|
output.push_back(Pair("address", address));
|
|
output.push_back(Pair("txid", it->first.txhash.GetHex()));
|
|
output.push_back(Pair("outputIndex", (int)it->first.index));
|
|
output.push_back(Pair("script", HexStr(it->second.script.begin(), it->second.script.end())));
|
|
if (p.IsValid())
|
|
{
|
|
CurrencyValuesAndNames(output, false, it->second.script, it->second.satoshis, friendlyNames);
|
|
}
|
|
output.push_back(Pair("satoshis", it->second.satoshis));
|
|
output.push_back(Pair("height", it->second.blockHeight));
|
|
if (chainActive.Height() >= it->second.blockHeight)
|
|
{
|
|
output.push_back(Pair("blocktime", chainActive[it->second.blockHeight]->GetBlockTime()));
|
|
}
|
|
utxos.push_back(output);
|
|
}
|
|
|
|
if (includeChainInfo) {
|
|
UniValue result(UniValue::VOBJ);
|
|
result.push_back(Pair("utxos", utxos));
|
|
|
|
LOCK(cs_main);
|
|
result.push_back(Pair("hash", chainActive.LastTip()->GetBlockHash().GetHex()));
|
|
result.push_back(Pair("height", (int)chainActive.Height()));
|
|
return result;
|
|
} else {
|
|
return utxos;
|
|
}
|
|
}
|
|
|
|
UniValue getaddressdeltas(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1 || !params[0].isObject())
|
|
throw runtime_error(
|
|
"getaddressdeltas\n"
|
|
"\nReturns all changes for an address (requires addressindex to be enabled).\n"
|
|
"\nArguments:\n"
|
|
"{\n"
|
|
" \"addresses\"\n"
|
|
" [\n"
|
|
" \"address\" (string) The base58check encoded address\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
" \"start\" (number) The start block height\n"
|
|
" \"end\" (number) The end block height\n"
|
|
" \"chaininfo\" (boolean) Include chain info in results, only applies if start and end specified\n"
|
|
" \"verbosity\" (number) Include additional currency data and values (0 or 1)\n"
|
|
" \"friendlynames\" (boolean) Include additional array of friendly names keyed by currency i-addresses\n"
|
|
"}\n"
|
|
"\nResult:\n"
|
|
"[\n"
|
|
" {\n"
|
|
" \"satoshis\" (number) The difference of satoshis\n"
|
|
" \"txid\" (string) The related txid\n"
|
|
" \"index\" (number) The related input or output index\n"
|
|
" \"height\" (number) The block height\n"
|
|
" \"address\" (string) The base58check encoded address\n"
|
|
" }\n"
|
|
"]\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'")
|
|
+ HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}")
|
|
);
|
|
|
|
|
|
UniValue startValue = find_value(params[0].get_obj(), "start");
|
|
UniValue endValue = find_value(params[0].get_obj(), "end");
|
|
|
|
bool includeChainInfo = uni_get_bool(find_value(params[0].get_obj(), "chaininfo"));
|
|
bool friendlyNames = uni_get_bool(find_value(params[0].get_obj(), "friendlynames"));
|
|
int verbosity = uni_get_bool(find_value(params[0].get_obj(), "verbosity"));
|
|
|
|
int start = 0;
|
|
int end = 0;
|
|
|
|
if (startValue.isNum() && endValue.isNum()) {
|
|
start = startValue.get_int();
|
|
end = endValue.get_int();
|
|
if (start <= 0 || end <= 0) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start and end is expected to be greater than zero");
|
|
}
|
|
if (end < start) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "End value is expected to be greater than start");
|
|
}
|
|
}
|
|
|
|
std::vector<std::pair<uint160, int> > addresses;
|
|
|
|
if (!getAddressesFromParams(params, addresses)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
|
|
|
{
|
|
LOCK(cs_main);
|
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
|
if (start > 0 && end > 0) {
|
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
|
}
|
|
} else {
|
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UniValue deltas(UniValue::VARR);
|
|
{
|
|
LOCK2(cs_main, mempool.cs);
|
|
|
|
CTransaction curTx;
|
|
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
|
|
std::string address;
|
|
if (!getAddressFromIndex(it->first.type, it->first.hashBytes, address)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
|
|
}
|
|
|
|
UniValue delta(UniValue::VOBJ);
|
|
delta.push_back(Pair("satoshis", it->second));
|
|
delta.push_back(Pair("txid", it->first.txhash.GetHex()));
|
|
delta.push_back(Pair("index", (int)it->first.index));
|
|
delta.push_back(Pair("blockindex", (int)it->first.txindex));
|
|
delta.push_back(Pair("height", it->first.blockHeight));
|
|
delta.push_back(Pair("address", address));
|
|
if (chainActive.Height() >= it->first.blockHeight)
|
|
{
|
|
delta.push_back(Pair("blocktime", chainActive[it->first.blockHeight]->GetBlockTime()));
|
|
}
|
|
|
|
uint256 blockHash;
|
|
if (verbosity && !it->first.txhash.IsNull() && (it->first.txhash == curTx.GetHash() || myGetTransaction(it->first.txhash, curTx, blockHash)))
|
|
{
|
|
CurrencyValuesAndNames(delta, it->first.spending, curTx, it->first.index, it->second, friendlyNames);
|
|
}
|
|
|
|
deltas.push_back(delta);
|
|
}
|
|
}
|
|
|
|
UniValue result(UniValue::VOBJ);
|
|
|
|
if (includeChainInfo && start > 0 && end > 0) {
|
|
LOCK(cs_main);
|
|
|
|
if (start > chainActive.Height() || end > chainActive.Height()) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start or end is outside chain range");
|
|
}
|
|
|
|
CBlockIndex* startIndex = chainActive[start];
|
|
CBlockIndex* endIndex = chainActive[end];
|
|
|
|
UniValue startInfo(UniValue::VOBJ);
|
|
UniValue endInfo(UniValue::VOBJ);
|
|
|
|
startInfo.push_back(Pair("hash", startIndex->GetBlockHash().GetHex()));
|
|
startInfo.push_back(Pair("height", start));
|
|
|
|
endInfo.push_back(Pair("hash", endIndex->GetBlockHash().GetHex()));
|
|
endInfo.push_back(Pair("height", end));
|
|
|
|
result.push_back(Pair("deltas", deltas));
|
|
result.push_back(Pair("start", startInfo));
|
|
result.push_back(Pair("end", endInfo));
|
|
|
|
return result;
|
|
} else {
|
|
return deltas;
|
|
}
|
|
}
|
|
|
|
UniValue getaddressbalance(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"getaddressbalance\n"
|
|
"\nReturns the balance for an address(es) (requires addressindex to be enabled).\n"
|
|
"\nArguments:\n"
|
|
"{\n"
|
|
" \"addresses\"\n"
|
|
" [\n"
|
|
" \"address\" (string) The base58check encoded address\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
" \"friendlynames\" (boolean) Include additional array of friendly names keyed by currency i-addresses\n"
|
|
"}\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"balance\" (number) The current balance in satoshis\n"
|
|
" \"received\" (number) The total number of satoshis received (including change)\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'")
|
|
+ HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}")
|
|
);
|
|
|
|
std::vector<std::pair<uint160, int> > addresses;
|
|
|
|
if (!getAddressesFromParams(params, addresses)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
bool friendlyNames = uni_get_bool(find_value(params[0].get_obj(), "friendlynames"));
|
|
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
|
|
|
LOCK2(cs_main, mempool.cs);
|
|
|
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
|
}
|
|
}
|
|
|
|
CTransaction curTx;
|
|
|
|
CAmount balance = 0;
|
|
CAmount received = 0;
|
|
|
|
CCurrencyValueMap reserveBalance;
|
|
CCurrencyValueMap reserveReceived;
|
|
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
|
|
uint256 blockHash;
|
|
if (!it->first.txhash.IsNull() && (it->first.txhash == curTx.GetHash() || myGetTransaction(it->first.txhash, curTx, blockHash)))
|
|
{
|
|
if (it->first.spending) {
|
|
CTransaction priorOutTx;
|
|
if (myGetTransaction(curTx.vin[it->first.index].prevout.hash, priorOutTx, blockHash))
|
|
{
|
|
reserveBalance -= priorOutTx.vout[curTx.vin[it->first.index].prevout.n].ReserveOutValue();
|
|
}
|
|
else
|
|
{
|
|
throw JSONRPCError(RPC_DATABASE_ERROR, "Unable to retrieve data for reserve output value");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
reserveBalance += curTx.vout[it->first.index].ReserveOutValue();
|
|
reserveReceived += curTx.vout[it->first.index].ReserveOutValue();
|
|
}
|
|
}
|
|
|
|
if (it->second > 0) {
|
|
received += it->second;
|
|
}
|
|
balance += it->second;
|
|
}
|
|
|
|
UniValue result(UniValue::VOBJ);
|
|
result.push_back(Pair("balance", balance));
|
|
result.push_back(Pair("received", received));
|
|
|
|
if (CConstVerusSolutionVector::GetVersionByHeight(chainActive.Height()) >= CActivationHeight::ACTIVATE_PBAAS)
|
|
{
|
|
if (balance || received)
|
|
{
|
|
reserveBalance.valueMap[ASSETCHAINS_CHAINID] = balance;
|
|
reserveReceived.valueMap[ASSETCHAINS_CHAINID] = received;
|
|
}
|
|
|
|
if (reserveBalance.valueMap.size())
|
|
{
|
|
UniValue currencyBal(UniValue::VOBJ);
|
|
for (auto &oneBalance : reserveBalance.valueMap)
|
|
{
|
|
std::string name = EncodeDestination(CIdentityID(oneBalance.first));
|
|
currencyBal.push_back(make_pair(name, ValueFromAmount(oneBalance.second)));
|
|
}
|
|
result.pushKV("currencybalance", currencyBal);
|
|
}
|
|
if (reserveReceived.valueMap.size())
|
|
{
|
|
UniValue currencyBal(UniValue::VOBJ);
|
|
UniValue currencyNames(UniValue::VOBJ);
|
|
for (auto &oneBalance : reserveReceived.valueMap)
|
|
{
|
|
std::string name = EncodeDestination(CIdentityID(oneBalance.first));
|
|
currencyBal.push_back(make_pair(name, ValueFromAmount(oneBalance.second)));
|
|
if (friendlyNames)
|
|
{
|
|
currencyNames.push_back(make_pair(name, ConnectedChains.GetFriendlyCurrencyName(oneBalance.first)));
|
|
}
|
|
}
|
|
result.pushKV("currencyreceived", currencyBal);
|
|
if (friendlyNames)
|
|
{
|
|
result.pushKV("currencynames", currencyNames);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
UniValue komodo_snapshot(int top);
|
|
|
|
UniValue getsnapshot(const UniValue& params, bool fHelp)
|
|
{
|
|
UniValue result(UniValue::VOBJ); int64_t total; int32_t top = 0;
|
|
|
|
if (params.size() > 0 && !params[0].isNull()) {
|
|
top = atoi(params[0].get_str().c_str());
|
|
if (top <= 0)
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, top must be a positive integer");
|
|
}
|
|
|
|
if ( fHelp || params.size() > 1)
|
|
{
|
|
throw runtime_error(
|
|
"getsnapshot\n"
|
|
"\nReturns a snapshot of (address,amount) pairs at current height (requires addressindex to be enabled).\n"
|
|
"\nArguments:\n"
|
|
" \"top\" (number, optional) Only return this many addresses, i.e. top N richlist\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"addresses\": [\n"
|
|
" {\n"
|
|
" \"addr\": \"RMEBhzvATA8mrfVK82E5TgPzzjtaggRGN3\",\n"
|
|
" \"amount\": \"100.0\"\n"
|
|
" },\n"
|
|
" {\n"
|
|
" \"addr\": \"RqEBhzvATAJmrfVL82E57gPzzjtaggR777\",\n"
|
|
" \"amount\": \"23.45\"\n"
|
|
" }\n"
|
|
" ],\n"
|
|
" \"total\": 123.45 (numeric) Total amount in snapshot\n"
|
|
" \"average\": 61.7, (numeric) Average amount in each address \n"
|
|
" \"utxos\": 14, (number) Total number of UTXOs in snapshot\n"
|
|
" \"total_addresses\": 2, (number) Total number of addresses in snapshot,\n"
|
|
" \"start_height\": 91, (number) Block height snapshot began\n"
|
|
" \"ending_height\": 91 (number) Block height snapsho finished,\n"
|
|
" \"start_time\": 1531982752, (number) Unix epoch time snapshot started\n"
|
|
" \"end_time\": 1531982752 (number) Unix epoch time snapshot finished\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getsnapshot","")
|
|
+ HelpExampleRpc("getsnapshot", "1000")
|
|
);
|
|
}
|
|
result = komodo_snapshot(top);
|
|
if ( result.size() > 0 ) {
|
|
result.push_back(Pair("end_time", (int) time(NULL)));
|
|
} else {
|
|
result.push_back(Pair("error", "no addressindex"));
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
UniValue getaddresstxids(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"getaddresstxids\n"
|
|
"\nReturns the txids for an address(es) (requires addressindex to be enabled).\n"
|
|
"\nArguments:\n"
|
|
"{\n"
|
|
" \"addresses\"\n"
|
|
" [\n"
|
|
" \"address\" (string) The base58check encoded address\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
" \"start\" (number) The start block height\n"
|
|
" \"end\" (number) The end block height\n"
|
|
"}\n"
|
|
"\nResult:\n"
|
|
"[\n"
|
|
" \"transactionid\" (string) The transaction id\n"
|
|
" ,...\n"
|
|
"]\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'")
|
|
+ HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}")
|
|
);
|
|
|
|
std::vector<std::pair<uint160, int> > addresses;
|
|
|
|
if (!getAddressesFromParams(params, addresses)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
|
|
int start = 0;
|
|
int end = 0;
|
|
if (params[0].isObject()) {
|
|
UniValue startValue = find_value(params[0].get_obj(), "start");
|
|
UniValue endValue = find_value(params[0].get_obj(), "end");
|
|
if (startValue.isNum() && endValue.isNum()) {
|
|
start = startValue.get_int();
|
|
end = endValue.get_int();
|
|
}
|
|
}
|
|
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
|
|
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
|
if (start > 0 && end > 0) {
|
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
|
}
|
|
} else {
|
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
|
}
|
|
}
|
|
}
|
|
|
|
std::set<std::pair<int, std::string> > txids;
|
|
UniValue result(UniValue::VARR);
|
|
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
|
|
int height = it->first.blockHeight;
|
|
std::string txid = it->first.txhash.GetHex();
|
|
|
|
if (addresses.size() > 1) {
|
|
txids.insert(std::make_pair(height, txid));
|
|
} else {
|
|
if (txids.insert(std::make_pair(height, txid)).second) {
|
|
result.push_back(txid);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (addresses.size() > 1) {
|
|
for (std::set<std::pair<int, std::string> >::const_iterator it=txids.begin(); it!=txids.end(); it++) {
|
|
result.push_back(it->second);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
UniValue getspentinfo(const UniValue& params, bool fHelp)
|
|
{
|
|
|
|
if (fHelp || params.size() != 1 || !params[0].isObject())
|
|
throw runtime_error(
|
|
"getspentinfo\n"
|
|
"\nReturns the txid and index where an output is spent.\n"
|
|
"\nArguments:\n"
|
|
"{\n"
|
|
" \"txid\" (string) The hex string of the txid\n"
|
|
" \"index\" (number) The start block height\n"
|
|
"}\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"txid\" (string) The transaction id\n"
|
|
" \"index\" (number) The spending input index\n"
|
|
" ,...\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getspentinfo", "'{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}'")
|
|
+ HelpExampleRpc("getspentinfo", "{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}")
|
|
);
|
|
|
|
UniValue txidValue = find_value(params[0].get_obj(), "txid");
|
|
UniValue indexValue = find_value(params[0].get_obj(), "index");
|
|
|
|
if (!txidValue.isStr() || !indexValue.isNum()) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid or index");
|
|
}
|
|
|
|
uint256 txid = ParseHashV(txidValue, "txid");
|
|
int outputIndex = indexValue.get_int();
|
|
|
|
CSpentIndexKey key(txid, outputIndex);
|
|
CSpentIndexValue value;
|
|
|
|
if (!GetSpentIndex(key, value)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get spent info");
|
|
}
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("txid", value.txid.GetHex()));
|
|
obj.push_back(Pair("index", (int)value.inputIndex));
|
|
obj.push_back(Pair("height", value.blockHeight));
|
|
|
|
return obj;
|
|
}
|
|
|
|
static const CRPCCommand commands[] =
|
|
{ // category name actor (function) okSafeMode
|
|
// --------------------- ------------------------ ----------------------- ----------
|
|
{ "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */
|
|
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
|
|
{ "util", "z_validateaddress", &z_validateaddress, true }, /* uses wallet if enabled */
|
|
{ "util", "createmultisig", &createmultisig, true },
|
|
{ "identity", "verifymessage", &verifymessage, true },
|
|
{ "identity", "verifyfile", &verifyfile, true },
|
|
{ "identity", "verifyhash", &verifyhash, true },
|
|
{ "vdxf", "getvdxfid", &getvdxfid, true },
|
|
{ "hidden", "hashdata", &hashdata, true }, // not visible in help
|
|
|
|
// START insightexplorer
|
|
/* Address index */
|
|
{ "addressindex", "getaddresstxids", &getaddresstxids, false }, /* insight explorer */
|
|
{ "addressindex", "getaddressbalance", &getaddressbalance, false }, /* insight explorer */
|
|
{ "addressindex", "getaddressdeltas", &getaddressdeltas, false }, /* insight explorer */
|
|
{ "addressindex", "getaddressutxos", &getaddressutxos, false }, /* insight explorer */
|
|
{ "addressindex", "getaddressmempool", &getaddressmempool, true }, /* insight explorer */
|
|
{ "blockchain", "getspentinfo", &getspentinfo, false }, /* insight explorer */
|
|
// END insightexplorer
|
|
|
|
/* Not shown in help */
|
|
{ "hidden", "setmocktime", &setmocktime, true },
|
|
};
|
|
|
|
void RegisterMiscRPCCommands(CRPCTable &tableRPC)
|
|
{
|
|
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
|
|
tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
|
|
}
|
|
|