7f687fd696
- add "spork" debug category - move "seen" and "unknown" to new "spork" debug category to reduce log spam - new/updated/seen messages should not overlap
983 lines
38 KiB
C++
983 lines
38 KiB
C++
// Copyright (c) 2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
|
// Copyright (c) 2014-2016 The Dash Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "base58.h"
|
|
#include "clientversion.h"
|
|
#include "init.h"
|
|
#include "main.h"
|
|
#include "net.h"
|
|
#include "netbase.h"
|
|
#include "rpcserver.h"
|
|
#include "timedata.h"
|
|
#include "txmempool.h"
|
|
#include "util.h"
|
|
#include "spork.h"
|
|
#include "utilstrencodings.h"
|
|
#ifdef ENABLE_WALLET
|
|
#include "masternode-sync.h"
|
|
#include "wallet/wallet.h"
|
|
#include "wallet/walletdb.h"
|
|
#endif
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <boost/assign/list_of.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <univalue.h>
|
|
|
|
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.
|
|
**/
|
|
UniValue getinfo(const UniValue& params, bool fHelp)
|
|
{
|
|
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"
|
|
" \"balance\": xxxxxxx, (numeric) the total dash balance of the wallet\n"
|
|
" \"privatesend_balance\": xxxxxx, (numeric) the anonymized dash balance of the wallet\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"
|
|
" \"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", "")
|
|
);
|
|
|
|
#ifdef ENABLE_WALLET
|
|
LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
|
|
#else
|
|
LOCK(cs_main);
|
|
#endif
|
|
|
|
proxyType proxy;
|
|
GetProxy(NET_IPV4, proxy);
|
|
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("version", CLIENT_VERSION));
|
|
obj.push_back(Pair("protocolversion", PROTOCOL_VERSION));
|
|
#ifdef ENABLE_WALLET
|
|
if (pwalletMain) {
|
|
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
|
|
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
|
|
if(!fLiteMode)
|
|
obj.push_back(Pair("privatesend_balance", ValueFromAmount(pwalletMain->GetAnonymizedBalance())));
|
|
}
|
|
#endif
|
|
obj.push_back(Pair("blocks", (int)chainActive.Height()));
|
|
obj.push_back(Pair("timeoffset", GetTimeOffset()));
|
|
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", Params().TestnetToBeDeprecatedFieldRPC()));
|
|
#ifdef ENABLE_WALLET
|
|
if (pwalletMain) {
|
|
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
|
|
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
|
|
obj.push_back(Pair("errors", GetWarnings("statusbar")));
|
|
return obj;
|
|
}
|
|
|
|
UniValue debug(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"debug ( 0|1|addrman|alert|bench|coindb|db|lock|rand|rpc|selectcoins|mempool"
|
|
"|mempoolrej|net|proxy|prune|http|libevent|tor|zmq|"
|
|
"dash|privatesend|instantsend|masternode|spork|keepass|mnpayments|gobject )\n"
|
|
"Change debug category on the fly. Specify single category or use comma to specify many.\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("debug", "dash")
|
|
+ HelpExampleRpc("debug", "dash,net")
|
|
);
|
|
|
|
std::string strMode = params[0].get_str();
|
|
|
|
mapMultiArgs["-debug"].clear();
|
|
boost::split(mapMultiArgs["-debug"], strMode, boost::is_any_of(","));
|
|
mapArgs["-debug"] = mapMultiArgs["-debug"][mapMultiArgs["-debug"].size() - 1];
|
|
|
|
fDebug = mapArgs["-debug"] != "0";
|
|
|
|
return "Debug mode: " + (fDebug ? strMode : "off");
|
|
}
|
|
|
|
UniValue mnsync(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"mnsync [status|next|reset]\n"
|
|
"Returns the sync status, updates to the next step or resets it entirely.\n"
|
|
);
|
|
|
|
std::string strMode = params[0].get_str();
|
|
|
|
if(strMode == "status") {
|
|
UniValue objStatus(UniValue::VOBJ);
|
|
objStatus.push_back(Pair("AssetID", masternodeSync.GetAssetID()));
|
|
objStatus.push_back(Pair("AssetName", masternodeSync.GetAssetName()));
|
|
objStatus.push_back(Pair("Attempt", masternodeSync.GetAttempt()));
|
|
objStatus.push_back(Pair("IsBlockchainSynced", masternodeSync.IsBlockchainSynced()));
|
|
objStatus.push_back(Pair("IsMasternodeListSynced", masternodeSync.IsMasternodeListSynced()));
|
|
objStatus.push_back(Pair("IsWinnersListSynced", masternodeSync.IsWinnersListSynced()));
|
|
objStatus.push_back(Pair("IsSynced", masternodeSync.IsSynced()));
|
|
objStatus.push_back(Pair("IsFailed", masternodeSync.IsFailed()));
|
|
return objStatus;
|
|
}
|
|
|
|
if(strMode == "next")
|
|
{
|
|
masternodeSync.SwitchToNextAsset();
|
|
return "sync updated to " + masternodeSync.GetAssetName();
|
|
}
|
|
|
|
if(strMode == "reset")
|
|
{
|
|
masternodeSync.Reset();
|
|
return "success";
|
|
}
|
|
return "failure";
|
|
}
|
|
|
|
#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)));
|
|
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
|
}
|
|
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);
|
|
BOOST_FOREACH(const CTxDestination& addr, addresses)
|
|
a.push_back(CBitcoinAddress(addr).ToString());
|
|
obj.push_back(Pair("addresses", a));
|
|
if (whichType == TX_MULTISIG)
|
|
obj.push_back(Pair("sigsrequired", nRequired));
|
|
}
|
|
return obj;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
Used for updating/reading spork settings on the network
|
|
*/
|
|
UniValue spork(const UniValue& params, bool fHelp)
|
|
{
|
|
if(params.size() == 1 && params[0].get_str() == "show"){
|
|
UniValue ret(UniValue::VOBJ);
|
|
for(int nSporkID = SPORK_START; nSporkID <= SPORK_END; nSporkID++){
|
|
if(sporkManager.GetSporkNameByID(nSporkID) != "Unknown")
|
|
ret.push_back(Pair(sporkManager.GetSporkNameByID(nSporkID), sporkManager.GetSporkValue(nSporkID)));
|
|
}
|
|
return ret;
|
|
} else if(params.size() == 1 && params[0].get_str() == "active"){
|
|
UniValue ret(UniValue::VOBJ);
|
|
for(int nSporkID = SPORK_START; nSporkID <= SPORK_END; nSporkID++){
|
|
if(sporkManager.GetSporkNameByID(nSporkID) != "Unknown")
|
|
ret.push_back(Pair(sporkManager.GetSporkNameByID(nSporkID), sporkManager.IsSporkActive(nSporkID)));
|
|
}
|
|
return ret;
|
|
} else if (params.size() == 2){
|
|
int nSporkID = sporkManager.GetSporkIDByName(params[0].get_str());
|
|
if(nSporkID == -1){
|
|
return "Invalid spork name";
|
|
}
|
|
|
|
// SPORK VALUE
|
|
int64_t nValue = params[1].get_int();
|
|
|
|
//broadcast new spork
|
|
if(sporkManager.UpdateSpork(nSporkID, nValue)){
|
|
sporkManager.ExecuteSpork(nSporkID, nValue);
|
|
return "success";
|
|
} else {
|
|
return "failure";
|
|
}
|
|
|
|
}
|
|
|
|
throw runtime_error(
|
|
"spork <name> [<value>]\n"
|
|
"<name> is the corresponding spork name, or 'show' to show all current spork settings, active to show which sporks are active"
|
|
"<value> is a epoch datetime to enable or disable spork"
|
|
+ HelpRequiringPassphrase());
|
|
}
|
|
|
|
UniValue validateaddress(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw runtime_error(
|
|
"validateaddress \"dashaddress\"\n"
|
|
"\nReturn information about the given dash address.\n"
|
|
"\nArguments:\n"
|
|
"1. \"dashaddress\" (string, required) The dash 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\" : \"dashaddress\", (string) The dash 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"
|
|
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\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", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"")
|
|
+ HelpExampleRpc("validateaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"")
|
|
);
|
|
|
|
#ifdef ENABLE_WALLET
|
|
LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
|
|
#else
|
|
LOCK(cs_main);
|
|
#endif
|
|
|
|
CBitcoinAddress address(params[0].get_str());
|
|
bool isValid = address.IsValid();
|
|
|
|
UniValue ret(UniValue::VOBJ);
|
|
ret.push_back(Pair("isvalid", isValid));
|
|
if (isValid)
|
|
{
|
|
CTxDestination dest = address.Get();
|
|
string currentAddress = address.ToString();
|
|
ret.push_back(Pair("address", currentAddress));
|
|
|
|
CScript scriptPubKey = GetScriptForDestination(dest);
|
|
ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
|
|
|
#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;
|
|
}
|
|
|
|
/**
|
|
* 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: Dash address and we have full public key:
|
|
CBitcoinAddress address(ks);
|
|
if (pwalletMain && address.IsValid())
|
|
{
|
|
CKeyID keyID;
|
|
if (!address.GetKeyID(keyID))
|
|
throw runtime_error(
|
|
strprintf("%s does not refer to a key",ks));
|
|
CPubKey vchPubKey;
|
|
if (!pwalletMain->GetPubKey(keyID, vchPubKey))
|
|
throw 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() > MAX_SCRIPT_ELEMENT_SIZE)
|
|
throw runtime_error(
|
|
strprintf("redeemScript exceeds size limit: %d > %d", result.size(), 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 dash addresses or hex-encoded public keys\n"
|
|
" [\n"
|
|
" \"key\" (string) dash 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 \"[\\\"Xt4qk9uKvQYAonVGSZNXqxeDmtjaEWgfrs\\\",\\\"XoSoWQkpgLpppPoyyzbUFh1fq2RBvW6UK1\\\"]\"") +
|
|
"\nAs a json rpc call\n"
|
|
+ HelpExampleRpc("createmultisig", "2, \"[\\\"Xt4qk9uKvQYAonVGSZNXqxeDmtjaEWgfrs\\\",\\\"XoSoWQkpgLpppPoyyzbUFh1fq2RBvW6UK1\\\"]\"")
|
|
;
|
|
throw runtime_error(msg);
|
|
}
|
|
|
|
// Construct using pay-to-script-hash:
|
|
CScript inner = _createmultisig_redeemScript(params);
|
|
CScriptID innerID(inner);
|
|
CBitcoinAddress address(innerID);
|
|
|
|
UniValue result(UniValue::VOBJ);
|
|
result.push_back(Pair("address", address.ToString()));
|
|
result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
|
|
|
|
return result;
|
|
}
|
|
|
|
UniValue verifymessage(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 3)
|
|
throw runtime_error(
|
|
"verifymessage \"dashaddress\" \"signature\" \"message\"\n"
|
|
"\nVerify a signed message\n"
|
|
"\nArguments:\n"
|
|
"1. \"dashaddress\" (string, required) The dash address to use for the signature.\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"
|
|
"\nResult:\n"
|
|
"true|false (boolean) If the signature is verified or not.\n"
|
|
"\nExamples:\n"
|
|
"\nUnlock the wallet for 30 seconds\n"
|
|
+ HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("signmessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"my message\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("verifymessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"signature\" \"my message\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("verifymessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", \"signature\", \"my message\"")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
string strAddress = params[0].get_str();
|
|
string strSign = params[1].get_str();
|
|
string strMessage = params[2].get_str();
|
|
|
|
CBitcoinAddress addr(strAddress);
|
|
if (!addr.IsValid())
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
|
|
|
|
CKeyID keyID;
|
|
if (!addr.GetKeyID(keyID))
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
|
|
|
|
bool fInvalid = false;
|
|
vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
|
|
if (fInvalid)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
ss << strMessageMagic;
|
|
ss << strMessage;
|
|
|
|
CPubKey pubkey;
|
|
if (!pubkey.RecoverCompact(ss.GetHash(), 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 == 2) {
|
|
address = CBitcoinAddress(CScriptID(hash)).ToString();
|
|
} else if (type == 1) {
|
|
address = CBitcoinAddress(CKeyID(hash)).ToString();
|
|
} 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 address");
|
|
}
|
|
addresses.push_back(std::make_pair(hashBytes, type));
|
|
}
|
|
} else {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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"
|
|
"}\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\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}'")
|
|
+ HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}")
|
|
);
|
|
|
|
std::vector<std::pair<uint160, int> > addresses;
|
|
|
|
if (!getAddressesFromParams(params, addresses)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
|
|
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("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 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"
|
|
"}\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\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}'")
|
|
+ HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}")
|
|
);
|
|
|
|
std::vector<std::pair<uint160, int> > addresses;
|
|
|
|
if (!getAddressesFromParams(params, addresses)) {
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
|
}
|
|
|
|
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 result(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;
|
|
if (!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())));
|
|
output.push_back(Pair("satoshis", it->second.satoshis));
|
|
output.push_back(Pair("height", it->second.blockHeight));
|
|
result.push_back(output);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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"
|
|
"}\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\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}'")
|
|
+ HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}")
|
|
);
|
|
|
|
|
|
UniValue startValue = find_value(params[0].get_obj(), "start");
|
|
UniValue endValue = find_value(params[0].get_obj(), "end");
|
|
|
|
int start = 0;
|
|
int end = 0;
|
|
|
|
if (startValue.isNum() && endValue.isNum()) {
|
|
start = startValue.get_int();
|
|
end = endValue.get_int();
|
|
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;
|
|
|
|
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 result(UniValue::VARR);
|
|
|
|
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));
|
|
result.push_back(delta);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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"
|
|
"}\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"balance\" (string) The current balance in satoshis\n"
|
|
" \"received\" (string) The total number of satoshis received (including change)\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}'")
|
|
+ HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}")
|
|
);
|
|
|
|
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;
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
CAmount balance = 0;
|
|
CAmount received = 0;
|
|
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
|
|
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));
|
|
|
|
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\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}'")
|
|
+ HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"]}")
|
|
);
|
|
|
|
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;
|
|
}
|