move rpc* to rpc/
This commit is contained in:
committed by
Jack Grigg
parent
34aca1b03c
commit
4519a766b6
1027
src/rpc/blockchain.cpp
Normal file
1027
src/rpc/blockchain.cpp
Normal file
File diff suppressed because it is too large
Load Diff
192
src/rpc/client.cpp
Normal file
192
src/rpc/client.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "rpc/client.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <set>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CRPCConvertParam
|
||||
{
|
||||
public:
|
||||
std::string methodName; //! method whose params want conversion
|
||||
int paramIdx; //! 0-based idx of param to convert
|
||||
};
|
||||
|
||||
static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{
|
||||
{ "stop", 0 },
|
||||
{ "setmocktime", 0 },
|
||||
{ "getaddednodeinfo", 0 },
|
||||
{ "setgenerate", 0 },
|
||||
{ "setgenerate", 1 },
|
||||
{ "generate", 0 },
|
||||
{ "getnetworkhashps", 0 },
|
||||
{ "getnetworkhashps", 1 },
|
||||
{ "getnetworksolps", 0 },
|
||||
{ "getnetworksolps", 1 },
|
||||
{ "sendtoaddress", 1 },
|
||||
{ "sendtoaddress", 4 },
|
||||
{ "settxfee", 0 },
|
||||
{ "getreceivedbyaddress", 1 },
|
||||
{ "getreceivedbyaccount", 1 },
|
||||
{ "listreceivedbyaddress", 0 },
|
||||
{ "listreceivedbyaddress", 1 },
|
||||
{ "listreceivedbyaddress", 2 },
|
||||
{ "listreceivedbyaccount", 0 },
|
||||
{ "listreceivedbyaccount", 1 },
|
||||
{ "listreceivedbyaccount", 2 },
|
||||
{ "getbalance", 1 },
|
||||
{ "getbalance", 2 },
|
||||
{ "getblockhash", 0 },
|
||||
{ "move", 2 },
|
||||
{ "move", 3 },
|
||||
{ "sendfrom", 2 },
|
||||
{ "sendfrom", 3 },
|
||||
{ "listtransactions", 1 },
|
||||
{ "listtransactions", 2 },
|
||||
{ "listtransactions", 3 },
|
||||
{ "listaccounts", 0 },
|
||||
{ "listaccounts", 1 },
|
||||
{ "walletpassphrase", 1 },
|
||||
{ "getblocktemplate", 0 },
|
||||
{ "listsinceblock", 1 },
|
||||
{ "listsinceblock", 2 },
|
||||
{ "sendmany", 1 },
|
||||
{ "sendmany", 2 },
|
||||
{ "sendmany", 4 },
|
||||
{ "addmultisigaddress", 0 },
|
||||
{ "addmultisigaddress", 1 },
|
||||
{ "createmultisig", 0 },
|
||||
{ "createmultisig", 1 },
|
||||
{ "listunspent", 0 },
|
||||
{ "listunspent", 1 },
|
||||
{ "listunspent", 2 },
|
||||
{ "getblock", 1 },
|
||||
{ "getblockheader", 1 },
|
||||
{ "gettransaction", 1 },
|
||||
{ "getrawtransaction", 1 },
|
||||
{ "createrawtransaction", 0 },
|
||||
{ "createrawtransaction", 1 },
|
||||
{ "createrawtransaction", 2 },
|
||||
{ "signrawtransaction", 1 },
|
||||
{ "signrawtransaction", 2 },
|
||||
{ "sendrawtransaction", 1 },
|
||||
{ "fundrawtransaction", 1 },
|
||||
{ "gettxout", 1 },
|
||||
{ "gettxout", 2 },
|
||||
{ "gettxoutproof", 0 },
|
||||
{ "lockunspent", 0 },
|
||||
{ "lockunspent", 1 },
|
||||
{ "importprivkey", 2 },
|
||||
{ "importaddress", 2 },
|
||||
{ "verifychain", 0 },
|
||||
{ "verifychain", 1 },
|
||||
{ "keypoolrefill", 0 },
|
||||
{ "getrawmempool", 0 },
|
||||
{ "estimatefee", 0 },
|
||||
{ "estimatepriority", 0 },
|
||||
{ "prioritisetransaction", 1 },
|
||||
{ "prioritisetransaction", 2 },
|
||||
{ "setban", 2 },
|
||||
{ "setban", 3 },
|
||||
{ "zcrawjoinsplit", 1 },
|
||||
{ "zcrawjoinsplit", 2 },
|
||||
{ "zcrawjoinsplit", 3 },
|
||||
{ "zcrawjoinsplit", 4 },
|
||||
{ "zcbenchmark", 1 },
|
||||
{ "zcbenchmark", 2 },
|
||||
{ "getblocksubsidy", 0},
|
||||
{ "z_listaddresses", 0},
|
||||
{ "z_listreceivedbyaddress", 1},
|
||||
{ "z_listunspent", 0 },
|
||||
{ "z_listunspent", 1 },
|
||||
{ "z_listunspent", 2 },
|
||||
{ "z_listunspent", 3 },
|
||||
{ "z_getbalance", 1},
|
||||
{ "z_gettotalbalance", 0},
|
||||
{ "z_gettotalbalance", 1},
|
||||
{ "z_gettotalbalance", 2},
|
||||
{ "z_mergetoaddress", 0},
|
||||
{ "z_mergetoaddress", 2},
|
||||
{ "z_mergetoaddress", 3},
|
||||
{ "z_mergetoaddress", 4},
|
||||
{ "z_sendmany", 1},
|
||||
{ "z_sendmany", 2},
|
||||
{ "z_sendmany", 3},
|
||||
{ "z_shieldcoinbase", 2},
|
||||
{ "z_shieldcoinbase", 3},
|
||||
{ "z_getoperationstatus", 0},
|
||||
{ "z_getoperationresult", 0},
|
||||
{ "z_importkey", 2 },
|
||||
{ "z_importviewingkey", 2 },
|
||||
{ "z_getpaymentdisclosure", 1},
|
||||
{ "z_getpaymentdisclosure", 2}
|
||||
};
|
||||
|
||||
class CRPCConvertTable
|
||||
{
|
||||
private:
|
||||
std::set<std::pair<std::string, int> > members;
|
||||
|
||||
public:
|
||||
CRPCConvertTable();
|
||||
|
||||
bool convert(const std::string& method, int idx) {
|
||||
return (members.count(std::make_pair(method, idx)) > 0);
|
||||
}
|
||||
};
|
||||
|
||||
CRPCConvertTable::CRPCConvertTable()
|
||||
{
|
||||
const unsigned int n_elem =
|
||||
(sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0]));
|
||||
|
||||
for (unsigned int i = 0; i < n_elem; i++) {
|
||||
members.insert(std::make_pair(vRPCConvertParams[i].methodName,
|
||||
vRPCConvertParams[i].paramIdx));
|
||||
}
|
||||
}
|
||||
|
||||
static CRPCConvertTable rpcCvtTable;
|
||||
|
||||
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
|
||||
* as well as objects and arrays.
|
||||
*/
|
||||
UniValue ParseNonRFCJSONValue(const std::string& strVal)
|
||||
{
|
||||
UniValue jVal;
|
||||
if (!jVal.read(std::string("[")+strVal+std::string("]")) ||
|
||||
!jVal.isArray() || jVal.size()!=1)
|
||||
throw runtime_error(string("Error parsing JSON:")+strVal);
|
||||
return jVal[0];
|
||||
}
|
||||
|
||||
/** Convert strings to command-specific RPC representation */
|
||||
UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
|
||||
{
|
||||
UniValue params(UniValue::VARR);
|
||||
|
||||
for (unsigned int idx = 0; idx < strParams.size(); idx++) {
|
||||
const std::string& strVal = strParams[idx];
|
||||
|
||||
if (!rpcCvtTable.convert(strMethod, idx)) {
|
||||
// insert string value directly
|
||||
params.push_back(strVal);
|
||||
} else {
|
||||
// parse string as JSON, insert bool/number/object/etc. value
|
||||
params.push_back(ParseNonRFCJSONValue(strVal));
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
17
src/rpc/client.h
Normal file
17
src/rpc/client.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_RPCCLIENT_H
|
||||
#define BITCOIN_RPCCLIENT_H
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
UniValue RPCConvertValues(const std::string& strMethod, const std::vector<std::string>& strParams);
|
||||
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
|
||||
* as well as objects and arrays.
|
||||
*/
|
||||
UniValue ParseNonRFCJSONValue(const std::string& strVal);
|
||||
|
||||
#endif // BITCOIN_RPCCLIENT_H
|
||||
912
src/rpc/mining.cpp
Normal file
912
src/rpc/mining.cpp
Normal file
@@ -0,0 +1,912 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "amount.h"
|
||||
#include "chainparams.h"
|
||||
#include "consensus/consensus.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "core_io.h"
|
||||
#ifdef ENABLE_MINING
|
||||
#include "crypto/equihash.h"
|
||||
#endif
|
||||
#include "init.h"
|
||||
#include "main.h"
|
||||
#include "metrics.h"
|
||||
#include "miner.h"
|
||||
#include "net.h"
|
||||
#include "pow.h"
|
||||
#include "rpc/server.h"
|
||||
#include "txmempool.h"
|
||||
#include "util.h"
|
||||
#include "validationinterface.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "wallet/wallet.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* Return average network hashes per second based on the last 'lookup' blocks,
|
||||
* or over the difficulty averaging window if 'lookup' is nonpositive.
|
||||
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
|
||||
*/
|
||||
int64_t GetNetworkHashPS(int lookup, int height) {
|
||||
CBlockIndex *pb = chainActive.Tip();
|
||||
|
||||
if (height >= 0 && height < chainActive.Height())
|
||||
pb = chainActive[height];
|
||||
|
||||
if (pb == NULL || !pb->nHeight)
|
||||
return 0;
|
||||
|
||||
// If lookup is nonpositive, then use difficulty averaging window.
|
||||
if (lookup <= 0)
|
||||
lookup = Params().GetConsensus().nPowAveragingWindow;
|
||||
|
||||
// If lookup is larger than chain, then set it to chain length.
|
||||
if (lookup > pb->nHeight)
|
||||
lookup = pb->nHeight;
|
||||
|
||||
CBlockIndex *pb0 = pb;
|
||||
int64_t minTime = pb0->GetBlockTime();
|
||||
int64_t maxTime = minTime;
|
||||
for (int i = 0; i < lookup; i++) {
|
||||
pb0 = pb0->pprev;
|
||||
int64_t time = pb0->GetBlockTime();
|
||||
minTime = std::min(time, minTime);
|
||||
maxTime = std::max(time, maxTime);
|
||||
}
|
||||
|
||||
// In case there's a situation where minTime == maxTime, we don't want a divide by zero exception.
|
||||
if (minTime == maxTime)
|
||||
return 0;
|
||||
|
||||
arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork;
|
||||
int64_t timeDiff = maxTime - minTime;
|
||||
|
||||
return (int64_t)(workDiff.getdouble() / timeDiff);
|
||||
}
|
||||
|
||||
UniValue getlocalsolps(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp)
|
||||
throw runtime_error(
|
||||
"getlocalsolps\n"
|
||||
"\nReturns the average local solutions per second since this node was started.\n"
|
||||
"This is the same information shown on the metrics screen (if enabled).\n"
|
||||
"\nResult:\n"
|
||||
"xxx.xxxxx (numeric) Solutions per second average\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getlocalsolps", "")
|
||||
+ HelpExampleRpc("getlocalsolps", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
return GetLocalSolPS();
|
||||
}
|
||||
|
||||
UniValue getnetworksolps(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"getnetworksolps ( blocks height )\n"
|
||||
"\nReturns the estimated network solutions per second based on the last n blocks.\n"
|
||||
"Pass in [blocks] to override # of blocks, -1 specifies over difficulty averaging window.\n"
|
||||
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n"
|
||||
"\nArguments:\n"
|
||||
"1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks over difficulty averaging window.\n"
|
||||
"2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n"
|
||||
"\nResult:\n"
|
||||
"x (numeric) Solutions per second estimated\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getnetworksolps", "")
|
||||
+ HelpExampleRpc("getnetworksolps", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1);
|
||||
}
|
||||
|
||||
UniValue getnetworkhashps(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"getnetworkhashps ( blocks height )\n"
|
||||
"\nDEPRECATED - left for backwards-compatibility. Use getnetworksolps instead.\n"
|
||||
"\nReturns the estimated network solutions per second based on the last n blocks.\n"
|
||||
"Pass in [blocks] to override # of blocks, -1 specifies over difficulty averaging window.\n"
|
||||
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n"
|
||||
"\nArguments:\n"
|
||||
"1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks over difficulty averaging window.\n"
|
||||
"2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n"
|
||||
"\nResult:\n"
|
||||
"x (numeric) Solutions per second estimated\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getnetworkhashps", "")
|
||||
+ HelpExampleRpc("getnetworkhashps", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MINING
|
||||
UniValue getgenerate(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getgenerate\n"
|
||||
"\nReturn if the server is set to generate coins or not. The default is false.\n"
|
||||
"It is set with the command line argument -gen (or zcash.conf setting gen)\n"
|
||||
"It can also be set with the setgenerate call.\n"
|
||||
"\nResult\n"
|
||||
"true|false (boolean) If the server is set to generate coins or not\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getgenerate", "")
|
||||
+ HelpExampleRpc("getgenerate", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
return GetBoolArg("-gen", false);
|
||||
}
|
||||
|
||||
UniValue generate(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"generate numblocks\n"
|
||||
"\nMine blocks immediately (before the RPC call returns)\n"
|
||||
"\nNote: this function can only be used on the regtest network\n"
|
||||
"\nArguments:\n"
|
||||
"1. numblocks (numeric) How many blocks are generated immediately.\n"
|
||||
"\nResult\n"
|
||||
"[ blockhashes ] (array) hashes of blocks generated\n"
|
||||
"\nExamples:\n"
|
||||
"\nGenerate 11 blocks\n"
|
||||
+ HelpExampleCli("generate", "11")
|
||||
);
|
||||
|
||||
if (GetArg("-mineraddress", "").empty()) {
|
||||
#ifdef ENABLE_WALLET
|
||||
if (!pwalletMain) {
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Wallet disabled and -mineraddress not set");
|
||||
}
|
||||
#else
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "zcashd compiled without wallet and -mineraddress not set");
|
||||
#endif
|
||||
}
|
||||
if (!Params().MineBlocksOnDemand())
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method can only be used on regtest");
|
||||
|
||||
int nHeightStart = 0;
|
||||
int nHeightEnd = 0;
|
||||
int nHeight = 0;
|
||||
int nGenerate = params[0].get_int();
|
||||
#ifdef ENABLE_WALLET
|
||||
CReserveKey reservekey(pwalletMain);
|
||||
#endif
|
||||
|
||||
{ // Don't keep cs_main locked
|
||||
LOCK(cs_main);
|
||||
nHeightStart = chainActive.Height();
|
||||
nHeight = nHeightStart;
|
||||
nHeightEnd = nHeightStart+nGenerate;
|
||||
}
|
||||
unsigned int nExtraNonce = 0;
|
||||
UniValue blockHashes(UniValue::VARR);
|
||||
unsigned int n = Params().EquihashN();
|
||||
unsigned int k = Params().EquihashK();
|
||||
while (nHeight < nHeightEnd)
|
||||
{
|
||||
#ifdef ENABLE_WALLET
|
||||
std::unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
|
||||
#else
|
||||
std::unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey());
|
||||
#endif
|
||||
if (!pblocktemplate.get())
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty");
|
||||
CBlock *pblock = &pblocktemplate->block;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
|
||||
}
|
||||
|
||||
// Hash state
|
||||
crypto_generichash_blake2b_state eh_state;
|
||||
EhInitialiseState(n, k, eh_state);
|
||||
|
||||
// I = the block header minus nonce and solution.
|
||||
CEquihashInput I{*pblock};
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << I;
|
||||
|
||||
// H(I||...
|
||||
crypto_generichash_blake2b_update(&eh_state, (unsigned char*)&ss[0], ss.size());
|
||||
|
||||
while (true) {
|
||||
// Yes, there is a chance every nonce could fail to satisfy the -regtest
|
||||
// target -- 1 in 2^(2^256). That ain't gonna happen
|
||||
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
|
||||
|
||||
// H(I||V||...
|
||||
crypto_generichash_blake2b_state curr_state;
|
||||
curr_state = eh_state;
|
||||
crypto_generichash_blake2b_update(&curr_state,
|
||||
pblock->nNonce.begin(),
|
||||
pblock->nNonce.size());
|
||||
|
||||
// (x_1, x_2, ...) = A(I, V, n, k)
|
||||
std::function<bool(std::vector<unsigned char>)> validBlock =
|
||||
[&pblock](std::vector<unsigned char> soln) {
|
||||
pblock->nSolution = soln;
|
||||
solutionTargetChecks.increment();
|
||||
return CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus());
|
||||
};
|
||||
bool found = EhBasicSolveUncancellable(n, k, curr_state, validBlock);
|
||||
ehSolverRuns.increment();
|
||||
if (found) {
|
||||
goto endloop;
|
||||
}
|
||||
}
|
||||
endloop:
|
||||
CValidationState state;
|
||||
if (!ProcessNewBlock(state, NULL, pblock, true, NULL))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
|
||||
++nHeight;
|
||||
blockHashes.push_back(pblock->GetHash().GetHex());
|
||||
}
|
||||
return blockHashes;
|
||||
}
|
||||
|
||||
|
||||
UniValue setgenerate(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"setgenerate generate ( genproclimit )\n"
|
||||
"\nSet 'generate' true or false to turn generation on or off.\n"
|
||||
"Generation is limited to 'genproclimit' processors, -1 is unlimited.\n"
|
||||
"See the getgenerate call for the current setting.\n"
|
||||
"\nArguments:\n"
|
||||
"1. generate (boolean, required) Set to true to turn on generation, off to turn off.\n"
|
||||
"2. genproclimit (numeric, optional) Set the processor limit for when generation is on. Can be -1 for unlimited.\n"
|
||||
"\nExamples:\n"
|
||||
"\nSet the generation on with a limit of one processor\n"
|
||||
+ HelpExampleCli("setgenerate", "true 1") +
|
||||
"\nCheck the setting\n"
|
||||
+ HelpExampleCli("getgenerate", "") +
|
||||
"\nTurn off generation\n"
|
||||
+ HelpExampleCli("setgenerate", "false") +
|
||||
"\nUsing json rpc\n"
|
||||
+ HelpExampleRpc("setgenerate", "true, 1")
|
||||
);
|
||||
|
||||
if (GetArg("-mineraddress", "").empty()) {
|
||||
#ifdef ENABLE_WALLET
|
||||
if (!pwalletMain) {
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Wallet disabled and -mineraddress not set");
|
||||
}
|
||||
#else
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "zcashd compiled without wallet and -mineraddress not set");
|
||||
#endif
|
||||
}
|
||||
if (Params().MineBlocksOnDemand())
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Use the generate method instead of setgenerate on this network");
|
||||
|
||||
bool fGenerate = true;
|
||||
if (params.size() > 0)
|
||||
fGenerate = params[0].get_bool();
|
||||
|
||||
int nGenProcLimit = -1;
|
||||
if (params.size() > 1)
|
||||
{
|
||||
nGenProcLimit = params[1].get_int();
|
||||
if (nGenProcLimit == 0)
|
||||
fGenerate = false;
|
||||
}
|
||||
|
||||
mapArgs["-gen"] = (fGenerate ? "1" : "0");
|
||||
mapArgs ["-genproclimit"] = itostr(nGenProcLimit);
|
||||
#ifdef ENABLE_WALLET
|
||||
GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit);
|
||||
#else
|
||||
GenerateBitcoins(fGenerate, nGenProcLimit);
|
||||
#endif
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
UniValue getmininginfo(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getmininginfo\n"
|
||||
"\nReturns a json object containing mining-related information."
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"blocks\": nnn, (numeric) The current block\n"
|
||||
" \"currentblocksize\": nnn, (numeric) The last block size\n"
|
||||
" \"currentblocktx\": nnn, (numeric) The last block transaction\n"
|
||||
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
|
||||
" \"errors\": \"...\" (string) Current errors\n"
|
||||
" \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n"
|
||||
" \"genproclimit\": n (numeric) The processor limit for generation. -1 if no generation. (see getgenerate or setgenerate calls)\n"
|
||||
" \"localsolps\": xxx.xxxxx (numeric) The average local solution rate in Sol/s since this node was started\n"
|
||||
" \"networksolps\": x (numeric) The estimated network solution rate in Sol/s\n"
|
||||
" \"pooledtx\": n (numeric) The size of the mem pool\n"
|
||||
" \"testnet\": true|false (boolean) If using testnet or not\n"
|
||||
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getmininginfo", "")
|
||||
+ HelpExampleRpc("getmininginfo", "")
|
||||
);
|
||||
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("blocks", (int)chainActive.Height()));
|
||||
obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
|
||||
obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
|
||||
obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty()));
|
||||
obj.push_back(Pair("errors", GetWarnings("statusbar")));
|
||||
obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1)));
|
||||
obj.push_back(Pair("localsolps" , getlocalsolps(params, false)));
|
||||
obj.push_back(Pair("networksolps", getnetworksolps(params, false)));
|
||||
obj.push_back(Pair("networkhashps", getnetworksolps(params, false)));
|
||||
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
|
||||
obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
|
||||
obj.push_back(Pair("chain", Params().NetworkIDString()));
|
||||
#ifdef ENABLE_MINING
|
||||
obj.push_back(Pair("generate", getgenerate(params, false)));
|
||||
#endif
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
|
||||
UniValue prioritisetransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 3)
|
||||
throw runtime_error(
|
||||
"prioritisetransaction <txid> <priority delta> <fee delta>\n"
|
||||
"Accepts the transaction into mined blocks at a higher (or lower) priority\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"txid\" (string, required) The transaction id.\n"
|
||||
"2. priority delta (numeric, required) The priority to add or subtract.\n"
|
||||
" The transaction selection algorithm considers the tx as it would have a higher priority.\n"
|
||||
" (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n"
|
||||
"3. fee delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n"
|
||||
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
|
||||
" considers the transaction as it would have paid a higher (or lower) fee.\n"
|
||||
"\nResult\n"
|
||||
"true (boolean) Returns true\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
|
||||
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
uint256 hash = ParseHashStr(params[0].get_str(), "txid");
|
||||
CAmount nAmount = params[2].get_int64();
|
||||
|
||||
mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
|
||||
static UniValue BIP22ValidationResult(const CValidationState& state)
|
||||
{
|
||||
if (state.IsValid())
|
||||
return NullUniValue;
|
||||
|
||||
std::string strRejectReason = state.GetRejectReason();
|
||||
if (state.IsError())
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
|
||||
if (state.IsInvalid())
|
||||
{
|
||||
if (strRejectReason.empty())
|
||||
return "rejected";
|
||||
return strRejectReason;
|
||||
}
|
||||
// Should be impossible
|
||||
return "valid?";
|
||||
}
|
||||
|
||||
UniValue getblocktemplate(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"getblocktemplate ( \"jsonrequestobject\" )\n"
|
||||
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
|
||||
"It returns data needed to construct a block to work on.\n"
|
||||
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n"
|
||||
" {\n"
|
||||
" \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n"
|
||||
" \"capabilities\":[ (array, optional) A list of strings\n"
|
||||
" \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"version\" : n, (numeric) The block version\n"
|
||||
" \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
|
||||
" \"finalsaplingroothash\" : \"xxxx\", (string) The hash of the final sapling root\n"
|
||||
" \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
|
||||
" {\n"
|
||||
" \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
|
||||
" \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n"
|
||||
" \"depends\" : [ (array) array of numbers \n"
|
||||
" n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
|
||||
" \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n"
|
||||
" \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
// " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
|
||||
// " \"flags\" : \"flags\" (string) \n"
|
||||
// " },\n"
|
||||
// " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n"
|
||||
" \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
|
||||
" \"target\" : \"xxxx\", (string) The hash target\n"
|
||||
" \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
" \"mutable\" : [ (array of string) list of ways the block template may be changed \n"
|
||||
" \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n"
|
||||
" \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n"
|
||||
" \"sizelimit\" : n, (numeric) limit of block size\n"
|
||||
" \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
" \"bits\" : \"xxx\", (string) compressed target of next block\n"
|
||||
" \"height\" : n (numeric) The height of the next block\n"
|
||||
"}\n"
|
||||
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getblocktemplate", "")
|
||||
+ HelpExampleRpc("getblocktemplate", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
// Wallet or miner address is required because we support coinbasetxn
|
||||
if (GetArg("-mineraddress", "").empty()) {
|
||||
#ifdef ENABLE_WALLET
|
||||
if (!pwalletMain) {
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Wallet disabled and -mineraddress not set");
|
||||
}
|
||||
#else
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "zcashd compiled without wallet and -mineraddress not set");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string strMode = "template";
|
||||
UniValue lpval = NullUniValue;
|
||||
// TODO: Re-enable coinbasevalue once a specification has been written
|
||||
bool coinbasetxn = true;
|
||||
if (params.size() > 0)
|
||||
{
|
||||
const UniValue& oparam = params[0].get_obj();
|
||||
const UniValue& modeval = find_value(oparam, "mode");
|
||||
if (modeval.isStr())
|
||||
strMode = modeval.get_str();
|
||||
else if (modeval.isNull())
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
else
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
|
||||
lpval = find_value(oparam, "longpollid");
|
||||
|
||||
if (strMode == "proposal")
|
||||
{
|
||||
const UniValue& dataval = find_value(oparam, "data");
|
||||
if (!dataval.isStr())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
|
||||
|
||||
CBlock block;
|
||||
if (!DecodeHexBlk(block, dataval.get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hash);
|
||||
if (mi != mapBlockIndex.end()) {
|
||||
CBlockIndex *pindex = mi->second;
|
||||
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
|
||||
return "duplicate";
|
||||
if (pindex->nStatus & BLOCK_FAILED_MASK)
|
||||
return "duplicate-invalid";
|
||||
return "duplicate-inconclusive";
|
||||
}
|
||||
|
||||
CBlockIndex* const pindexPrev = chainActive.Tip();
|
||||
// TestBlockValidity only supports blocks built on the current Tip
|
||||
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
|
||||
return "inconclusive-not-best-prevblk";
|
||||
CValidationState state;
|
||||
TestBlockValidity(state, block, pindexPrev, false, true);
|
||||
return BIP22ValidationResult(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (strMode != "template")
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
|
||||
|
||||
if (vNodes.empty())
|
||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Zcash is not connected!");
|
||||
|
||||
if (IsInitialBlockDownload())
|
||||
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zcash is downloading blocks...");
|
||||
|
||||
static unsigned int nTransactionsUpdatedLast;
|
||||
|
||||
if (!lpval.isNull())
|
||||
{
|
||||
// Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
|
||||
uint256 hashWatchedChain;
|
||||
boost::system_time checktxtime;
|
||||
unsigned int nTransactionsUpdatedLastLP;
|
||||
|
||||
if (lpval.isStr())
|
||||
{
|
||||
// Format: <hashBestChain><nTransactionsUpdatedLast>
|
||||
std::string lpstr = lpval.get_str();
|
||||
|
||||
hashWatchedChain.SetHex(lpstr.substr(0, 64));
|
||||
nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
|
||||
hashWatchedChain = chainActive.Tip()->GetBlockHash();
|
||||
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
|
||||
}
|
||||
|
||||
// Release the wallet and main lock while waiting
|
||||
LEAVE_CRITICAL_SECTION(cs_main);
|
||||
{
|
||||
checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(csBestBlock);
|
||||
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
|
||||
{
|
||||
if (!cvBlockChange.timed_wait(lock, checktxtime))
|
||||
{
|
||||
// Timeout: Check transactions for update
|
||||
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
|
||||
break;
|
||||
checktxtime += boost::posix_time::seconds(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
ENTER_CRITICAL_SECTION(cs_main);
|
||||
|
||||
if (!IsRPCRunning())
|
||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
|
||||
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
|
||||
}
|
||||
|
||||
// Update block
|
||||
static CBlockIndex* pindexPrev;
|
||||
static int64_t nStart;
|
||||
static CBlockTemplate* pblocktemplate;
|
||||
if (pindexPrev != chainActive.Tip() ||
|
||||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
|
||||
{
|
||||
// Clear pindexPrev so future calls make a new block, despite any failures from here on
|
||||
pindexPrev = NULL;
|
||||
|
||||
// Store the pindexBest used before CreateNewBlockWithKey, to avoid races
|
||||
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
|
||||
CBlockIndex* pindexPrevNew = chainActive.Tip();
|
||||
nStart = GetTime();
|
||||
|
||||
// Create new block
|
||||
if(pblocktemplate)
|
||||
{
|
||||
delete pblocktemplate;
|
||||
pblocktemplate = NULL;
|
||||
}
|
||||
#ifdef ENABLE_WALLET
|
||||
CReserveKey reservekey(pwalletMain);
|
||||
pblocktemplate = CreateNewBlockWithKey(reservekey);
|
||||
#else
|
||||
pblocktemplate = CreateNewBlockWithKey();
|
||||
#endif
|
||||
if (!pblocktemplate)
|
||||
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
||||
|
||||
// Need to update only after we know CreateNewBlockWithKey succeeded
|
||||
pindexPrev = pindexPrevNew;
|
||||
}
|
||||
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
|
||||
|
||||
// Update nTime
|
||||
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
|
||||
pblock->nNonce = uint256();
|
||||
|
||||
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
|
||||
|
||||
UniValue txCoinbase = NullUniValue;
|
||||
UniValue transactions(UniValue::VARR);
|
||||
map<uint256, int64_t> setTxIndex;
|
||||
int i = 0;
|
||||
BOOST_FOREACH (const CTransaction& tx, pblock->vtx) {
|
||||
uint256 txHash = tx.GetHash();
|
||||
setTxIndex[txHash] = i++;
|
||||
|
||||
if (tx.IsCoinBase() && !coinbasetxn)
|
||||
continue;
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
|
||||
entry.push_back(Pair("data", EncodeHexTx(tx)));
|
||||
|
||||
entry.push_back(Pair("hash", txHash.GetHex()));
|
||||
|
||||
UniValue deps(UniValue::VARR);
|
||||
BOOST_FOREACH (const CTxIn &in, tx.vin)
|
||||
{
|
||||
if (setTxIndex.count(in.prevout.hash))
|
||||
deps.push_back(setTxIndex[in.prevout.hash]);
|
||||
}
|
||||
entry.push_back(Pair("depends", deps));
|
||||
|
||||
int index_in_template = i - 1;
|
||||
entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
|
||||
entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template]));
|
||||
|
||||
if (tx.IsCoinBase()) {
|
||||
// Show founders' reward if it is required
|
||||
if (pblock->vtx[0].vout.size() > 1) {
|
||||
// Correct this if GetBlockTemplate changes the order
|
||||
entry.push_back(Pair("foundersreward", (int64_t)tx.vout[1].nValue));
|
||||
}
|
||||
entry.push_back(Pair("required", true));
|
||||
txCoinbase = entry;
|
||||
} else {
|
||||
transactions.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
UniValue aux(UniValue::VOBJ);
|
||||
aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
|
||||
|
||||
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
|
||||
|
||||
static UniValue aMutable(UniValue::VARR);
|
||||
if (aMutable.empty())
|
||||
{
|
||||
aMutable.push_back("time");
|
||||
aMutable.push_back("transactions");
|
||||
aMutable.push_back("prevblock");
|
||||
}
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("capabilities", aCaps));
|
||||
result.push_back(Pair("version", pblock->nVersion));
|
||||
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
|
||||
result.push_back(Pair("finalsaplingroothash", pblock->hashFinalSaplingRoot.GetHex()));
|
||||
result.push_back(Pair("transactions", transactions));
|
||||
if (coinbasetxn) {
|
||||
assert(txCoinbase.isObject());
|
||||
result.push_back(Pair("coinbasetxn", txCoinbase));
|
||||
} else {
|
||||
result.push_back(Pair("coinbaseaux", aux));
|
||||
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
|
||||
}
|
||||
result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
|
||||
result.push_back(Pair("target", hashTarget.GetHex()));
|
||||
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
|
||||
result.push_back(Pair("mutable", aMutable));
|
||||
result.push_back(Pair("noncerange", "00000000ffffffff"));
|
||||
result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
|
||||
result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
|
||||
result.push_back(Pair("curtime", pblock->GetBlockTime()));
|
||||
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
|
||||
result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class submitblock_StateCatcher : public CValidationInterface
|
||||
{
|
||||
public:
|
||||
uint256 hash;
|
||||
bool found;
|
||||
CValidationState state;
|
||||
|
||||
submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {};
|
||||
|
||||
protected:
|
||||
virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
|
||||
if (block.GetHash() != hash)
|
||||
return;
|
||||
found = true;
|
||||
state = stateIn;
|
||||
};
|
||||
};
|
||||
|
||||
UniValue submitblock(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"submitblock \"hexdata\" ( \"jsonparametersobject\" )\n"
|
||||
"\nAttempts to submit new block to network.\n"
|
||||
"The 'jsonparametersobject' parameter is currently ignored.\n"
|
||||
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
|
||||
|
||||
"\nArguments\n"
|
||||
"1. \"hexdata\" (string, required) the hex-encoded block data to submit\n"
|
||||
"2. \"jsonparametersobject\" (string, optional) object of optional parameters\n"
|
||||
" {\n"
|
||||
" \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n"
|
||||
" }\n"
|
||||
"\nResult:\n"
|
||||
"\"duplicate\" - node already has valid copy of block\n"
|
||||
"\"duplicate-invalid\" - node already has block, but it is invalid\n"
|
||||
"\"duplicate-inconclusive\" - node already has block but has not validated it\n"
|
||||
"\"inconclusive\" - node has not validated the block, it may not be on the node's current best chain\n"
|
||||
"\"rejected\" - block was rejected as invalid\n"
|
||||
"For more information on submitblock parameters and results, see: https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki#block-submission\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("submitblock", "\"mydata\"")
|
||||
+ HelpExampleRpc("submitblock", "\"mydata\"")
|
||||
);
|
||||
|
||||
CBlock block;
|
||||
if (!DecodeHexBlk(block, params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
bool fBlockPresent = false;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hash);
|
||||
if (mi != mapBlockIndex.end()) {
|
||||
CBlockIndex *pindex = mi->second;
|
||||
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
|
||||
return "duplicate";
|
||||
if (pindex->nStatus & BLOCK_FAILED_MASK)
|
||||
return "duplicate-invalid";
|
||||
// Otherwise, we might only have the header - process the block before returning
|
||||
fBlockPresent = true;
|
||||
}
|
||||
}
|
||||
|
||||
CValidationState state;
|
||||
submitblock_StateCatcher sc(block.GetHash());
|
||||
RegisterValidationInterface(&sc);
|
||||
bool fAccepted = ProcessNewBlock(state, NULL, &block, true, NULL);
|
||||
UnregisterValidationInterface(&sc);
|
||||
if (fBlockPresent)
|
||||
{
|
||||
if (fAccepted && !sc.found)
|
||||
return "duplicate-inconclusive";
|
||||
return "duplicate";
|
||||
}
|
||||
if (fAccepted)
|
||||
{
|
||||
if (!sc.found)
|
||||
return "inconclusive";
|
||||
state = sc.state;
|
||||
}
|
||||
return BIP22ValidationResult(state);
|
||||
}
|
||||
|
||||
UniValue estimatefee(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"estimatefee nblocks\n"
|
||||
"\nEstimates the approximate fee per kilobyte\n"
|
||||
"needed for a transaction to begin confirmation\n"
|
||||
"within nblocks blocks.\n"
|
||||
"\nArguments:\n"
|
||||
"1. nblocks (numeric)\n"
|
||||
"\nResult:\n"
|
||||
"n : (numeric) estimated fee-per-kilobyte\n"
|
||||
"\n"
|
||||
"-1.0 is returned if not enough transactions and\n"
|
||||
"blocks have been observed to make an estimate.\n"
|
||||
"\nExample:\n"
|
||||
+ HelpExampleCli("estimatefee", "6")
|
||||
);
|
||||
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
|
||||
|
||||
int nBlocks = params[0].get_int();
|
||||
if (nBlocks < 1)
|
||||
nBlocks = 1;
|
||||
|
||||
CFeeRate feeRate = mempool.estimateFee(nBlocks);
|
||||
if (feeRate == CFeeRate(0))
|
||||
return -1.0;
|
||||
|
||||
return ValueFromAmount(feeRate.GetFeePerK());
|
||||
}
|
||||
|
||||
UniValue estimatepriority(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"estimatepriority nblocks\n"
|
||||
"\nEstimates the approximate priority\n"
|
||||
"a zero-fee transaction needs to begin confirmation\n"
|
||||
"within nblocks blocks.\n"
|
||||
"\nArguments:\n"
|
||||
"1. nblocks (numeric)\n"
|
||||
"\nResult:\n"
|
||||
"n : (numeric) estimated priority\n"
|
||||
"\n"
|
||||
"-1.0 is returned if not enough transactions and\n"
|
||||
"blocks have been observed to make an estimate.\n"
|
||||
"\nExample:\n"
|
||||
+ HelpExampleCli("estimatepriority", "6")
|
||||
);
|
||||
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
|
||||
|
||||
int nBlocks = params[0].get_int();
|
||||
if (nBlocks < 1)
|
||||
nBlocks = 1;
|
||||
|
||||
return mempool.estimatePriority(nBlocks);
|
||||
}
|
||||
|
||||
UniValue getblocksubsidy(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"getblocksubsidy height\n"
|
||||
"\nReturns block subsidy reward, taking into account the mining slow start and the founders reward, of block at index provided.\n"
|
||||
"\nArguments:\n"
|
||||
"1. height (numeric, optional) The block height. If not provided, defaults to the current height of the chain.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"miner\" : x.xxx (numeric) The mining reward amount in " + CURRENCY_UNIT + ".\n"
|
||||
" \"founders\" : x.xxx (numeric) The founders reward amount in " + CURRENCY_UNIT + ".\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getblocksubsidy", "1000")
|
||||
+ HelpExampleRpc("getblockubsidy", "1000")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
int nHeight = (params.size()==1) ? params[0].get_int() : chainActive.Height();
|
||||
if (nHeight < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
|
||||
|
||||
CAmount nReward = GetBlockSubsidy(nHeight, Params().GetConsensus());
|
||||
CAmount nFoundersReward = 0;
|
||||
if ((nHeight > 0) && (nHeight <= Params().GetConsensus().GetLastFoundersRewardBlockHeight())) {
|
||||
nFoundersReward = nReward/5;
|
||||
nReward -= nFoundersReward;
|
||||
}
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("miner", ValueFromAmount(nReward)));
|
||||
result.push_back(Pair("founders", ValueFromAmount(nFoundersReward)));
|
||||
return result;
|
||||
}
|
||||
484
src/rpc/misc.cpp
Normal file
484
src/rpc/misc.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#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 "util.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "wallet/wallet.h"
|
||||
#include "wallet/walletdb.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
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 Zcash 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())));
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
#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);
|
||||
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;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
UniValue validateaddress(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"validateaddress \"zcashaddress\"\n"
|
||||
"\nReturn information about the given Zcash address.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"zcashaddress\" (string, required) The Zcash 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\" : \"zcashaddress\", (string) The Zcash 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", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
||||
+ HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
||||
);
|
||||
|
||||
#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())));
|
||||
|
||||
#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->HaveSpendingKey(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::SaplingFullViewingKey fvk;
|
||||
bool isMine = pwalletMain->GetSaplingIncomingViewingKey(zaddr, ivk) &&
|
||||
pwalletMain->GetSaplingFullViewingKey(ivk, fvk) &&
|
||||
pwalletMain->HaveSaplingSpendingKey(fvk);
|
||||
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() > 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 Zcash addresses or hex-encoded public keys\n"
|
||||
" [\n"
|
||||
" \"key\" (string) Zcash 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 \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
|
||||
"\nAs a json rpc call\n"
|
||||
+ HelpExampleRpc("createmultisig", "2, \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")
|
||||
;
|
||||
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;
|
||||
}
|
||||
|
||||
UniValue verifymessage(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 3)
|
||||
throw runtime_error(
|
||||
"verifymessage \"zcashaddress\" \"signature\" \"message\"\n"
|
||||
"\nVerify a signed message\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"zcashaddress\" (string, required) The Zcash 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", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"my message\"") +
|
||||
"\nVerify the signature\n"
|
||||
+ HelpExampleCli("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"signature\" \"my message\"") +
|
||||
"\nAs json rpc\n"
|
||||
+ HelpExampleRpc("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"signature\", \"my message\"")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
string strAddress = params[0].get_str();
|
||||
string strSign = params[1].get_str();
|
||||
string strMessage = params[2].get_str();
|
||||
|
||||
CTxDestination destination = DecodeDestination(strAddress);
|
||||
if (!IsValidDestination(destination)) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
|
||||
}
|
||||
|
||||
const CKeyID *keyID = boost::get<CKeyID>(&destination);
|
||||
if (!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;
|
||||
}
|
||||
600
src/rpc/net.cpp
Normal file
600
src/rpc/net.cpp
Normal file
@@ -0,0 +1,600 @@
|
||||
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "rpc/server.h"
|
||||
|
||||
#include "clientversion.h"
|
||||
#include "main.h"
|
||||
#include "net.h"
|
||||
#include "netbase.h"
|
||||
#include "protocol.h"
|
||||
#include "sync.h"
|
||||
#include "timedata.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "deprecation.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
UniValue getconnectioncount(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getconnectioncount\n"
|
||||
"\nReturns the number of connections to other nodes.\n"
|
||||
"\nbResult:\n"
|
||||
"n (numeric) The connection count\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getconnectioncount", "")
|
||||
+ HelpExampleRpc("getconnectioncount", "")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, cs_vNodes);
|
||||
|
||||
return (int)vNodes.size();
|
||||
}
|
||||
|
||||
UniValue ping(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"ping\n"
|
||||
"\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
|
||||
"Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
|
||||
"Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("ping", "")
|
||||
+ HelpExampleRpc("ping", "")
|
||||
);
|
||||
|
||||
// Request that each node send a ping during next message processing pass
|
||||
LOCK2(cs_main, cs_vNodes);
|
||||
|
||||
BOOST_FOREACH(CNode* pNode, vNodes) {
|
||||
pNode->fPingQueued = true;
|
||||
}
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
static void CopyNodeStats(std::vector<CNodeStats>& vstats)
|
||||
{
|
||||
vstats.clear();
|
||||
|
||||
LOCK(cs_vNodes);
|
||||
vstats.reserve(vNodes.size());
|
||||
BOOST_FOREACH(CNode* pnode, vNodes) {
|
||||
CNodeStats stats;
|
||||
pnode->copyStats(stats);
|
||||
vstats.push_back(stats);
|
||||
}
|
||||
}
|
||||
|
||||
UniValue getpeerinfo(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getpeerinfo\n"
|
||||
"\nReturns data about each connected network node as a json array of objects.\n"
|
||||
"\nbResult:\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
" \"id\": n, (numeric) Peer index\n"
|
||||
" \"addr\":\"host:port\", (string) The ip address and port of the peer\n"
|
||||
" \"addrlocal\":\"ip:port\", (string) local address\n"
|
||||
" \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n"
|
||||
" \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n"
|
||||
" \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n"
|
||||
" \"bytessent\": n, (numeric) The total bytes sent\n"
|
||||
" \"bytesrecv\": n, (numeric) The total bytes received\n"
|
||||
" \"conntime\": ttt, (numeric) The connection time in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
" \"timeoffset\": ttt, (numeric) The time offset in seconds\n"
|
||||
" \"pingtime\": n, (numeric) ping time\n"
|
||||
" \"pingwait\": n, (numeric) ping wait\n"
|
||||
" \"version\": v, (numeric) The peer version, such as 170002\n"
|
||||
" \"subver\": \"/MagicBean:x.y.z[-v]/\", (string) The string version\n"
|
||||
" \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n"
|
||||
" \"startingheight\": n, (numeric) The starting height (block) of the peer\n"
|
||||
" \"banscore\": n, (numeric) The ban score\n"
|
||||
" \"synced_headers\": n, (numeric) The last header we have in common with this peer\n"
|
||||
" \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n"
|
||||
" \"inflight\": [\n"
|
||||
" n, (numeric) The heights of blocks we're currently asking from this peer\n"
|
||||
" ...\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getpeerinfo", "")
|
||||
+ HelpExampleRpc("getpeerinfo", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
vector<CNodeStats> vstats;
|
||||
CopyNodeStats(vstats);
|
||||
|
||||
UniValue ret(UniValue::VARR);
|
||||
|
||||
BOOST_FOREACH(const CNodeStats& stats, vstats) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CNodeStateStats statestats;
|
||||
bool fStateStats = GetNodeStateStats(stats.nodeid, statestats);
|
||||
obj.push_back(Pair("id", stats.nodeid));
|
||||
obj.push_back(Pair("addr", stats.addrName));
|
||||
if (!(stats.addrLocal.empty()))
|
||||
obj.push_back(Pair("addrlocal", stats.addrLocal));
|
||||
obj.push_back(Pair("services", strprintf("%016x", stats.nServices)));
|
||||
obj.push_back(Pair("lastsend", stats.nLastSend));
|
||||
obj.push_back(Pair("lastrecv", stats.nLastRecv));
|
||||
obj.push_back(Pair("bytessent", stats.nSendBytes));
|
||||
obj.push_back(Pair("bytesrecv", stats.nRecvBytes));
|
||||
obj.push_back(Pair("conntime", stats.nTimeConnected));
|
||||
obj.push_back(Pair("timeoffset", stats.nTimeOffset));
|
||||
obj.push_back(Pair("pingtime", stats.dPingTime));
|
||||
if (stats.dPingWait > 0.0)
|
||||
obj.push_back(Pair("pingwait", stats.dPingWait));
|
||||
obj.push_back(Pair("version", stats.nVersion));
|
||||
// Use the sanitized form of subver here, to avoid tricksy remote peers from
|
||||
// corrupting or modifying the JSON output by putting special characters in
|
||||
// their ver message.
|
||||
obj.push_back(Pair("subver", stats.cleanSubVer));
|
||||
obj.push_back(Pair("inbound", stats.fInbound));
|
||||
obj.push_back(Pair("startingheight", stats.nStartingHeight));
|
||||
if (fStateStats) {
|
||||
obj.push_back(Pair("banscore", statestats.nMisbehavior));
|
||||
obj.push_back(Pair("synced_headers", statestats.nSyncHeight));
|
||||
obj.push_back(Pair("synced_blocks", statestats.nCommonHeight));
|
||||
UniValue heights(UniValue::VARR);
|
||||
BOOST_FOREACH(int height, statestats.vHeightInFlight) {
|
||||
heights.push_back(height);
|
||||
}
|
||||
obj.push_back(Pair("inflight", heights));
|
||||
}
|
||||
obj.push_back(Pair("whitelisted", stats.fWhitelisted));
|
||||
|
||||
ret.push_back(obj);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue addnode(const UniValue& params, bool fHelp)
|
||||
{
|
||||
string strCommand;
|
||||
if (params.size() == 2)
|
||||
strCommand = params[1].get_str();
|
||||
if (fHelp || params.size() != 2 ||
|
||||
(strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
|
||||
throw runtime_error(
|
||||
"addnode \"node\" \"add|remove|onetry\"\n"
|
||||
"\nAttempts add or remove a node from the addnode list.\n"
|
||||
"Or try a connection to a node once.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
|
||||
"2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("addnode", "\"192.168.0.6:8233\" \"onetry\"")
|
||||
+ HelpExampleRpc("addnode", "\"192.168.0.6:8233\", \"onetry\"")
|
||||
);
|
||||
|
||||
string strNode = params[0].get_str();
|
||||
|
||||
if (strCommand == "onetry")
|
||||
{
|
||||
CAddress addr;
|
||||
OpenNetworkConnection(addr, NULL, strNode.c_str());
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
LOCK(cs_vAddedNodes);
|
||||
vector<string>::iterator it = vAddedNodes.begin();
|
||||
for(; it != vAddedNodes.end(); it++)
|
||||
if (strNode == *it)
|
||||
break;
|
||||
|
||||
if (strCommand == "add")
|
||||
{
|
||||
if (it != vAddedNodes.end())
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
|
||||
vAddedNodes.push_back(strNode);
|
||||
}
|
||||
else if(strCommand == "remove")
|
||||
{
|
||||
if (it == vAddedNodes.end())
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
|
||||
vAddedNodes.erase(it);
|
||||
}
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue disconnectnode(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"disconnectnode \"node\" \n"
|
||||
"\nImmediately disconnects from the specified node.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8233\"")
|
||||
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8233\"")
|
||||
);
|
||||
|
||||
CNode* pNode = FindNode(params[0].get_str());
|
||||
if (pNode == NULL)
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
|
||||
|
||||
pNode->fDisconnect = true;
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue getaddednodeinfo(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"getaddednodeinfo dns ( \"node\" )\n"
|
||||
"\nReturns information about the given added node, or all added nodes\n"
|
||||
"(note that onetry addnodes are not listed here)\n"
|
||||
"If dns is false, only a list of added nodes will be provided,\n"
|
||||
"otherwise connected information will also be available.\n"
|
||||
"\nArguments:\n"
|
||||
"1. dns (boolean, required) If false, only a list of added nodes will be provided, otherwise connected information will also be available.\n"
|
||||
"2. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n"
|
||||
"\nResult:\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
" \"addednode\" : \"192.168.0.201\", (string) The node ip address\n"
|
||||
" \"connected\" : true|false, (boolean) If connected\n"
|
||||
" \"addresses\" : [\n"
|
||||
" {\n"
|
||||
" \"address\" : \"192.168.0.201:8233\", (string) The Zcash server host and port\n"
|
||||
" \"connected\" : \"outbound\" (string) connection, inbound or outbound\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getaddednodeinfo", "true")
|
||||
+ HelpExampleCli("getaddednodeinfo", "true \"192.168.0.201\"")
|
||||
+ HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"")
|
||||
);
|
||||
|
||||
bool fDns = params[0].get_bool();
|
||||
|
||||
list<string> laddedNodes(0);
|
||||
if (params.size() == 1)
|
||||
{
|
||||
LOCK(cs_vAddedNodes);
|
||||
BOOST_FOREACH(const std::string& strAddNode, vAddedNodes)
|
||||
laddedNodes.push_back(strAddNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
string strNode = params[1].get_str();
|
||||
LOCK(cs_vAddedNodes);
|
||||
BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) {
|
||||
if (strAddNode == strNode)
|
||||
{
|
||||
laddedNodes.push_back(strAddNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (laddedNodes.size() == 0)
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VARR);
|
||||
if (!fDns)
|
||||
{
|
||||
BOOST_FOREACH (const std::string& strAddNode, laddedNodes) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("addednode", strAddNode));
|
||||
ret.push_back(obj);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
list<pair<string, vector<CService> > > laddedAddreses(0);
|
||||
BOOST_FOREACH(const std::string& strAddNode, laddedNodes) {
|
||||
vector<CService> vservNode(0);
|
||||
if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0))
|
||||
laddedAddreses.push_back(make_pair(strAddNode, vservNode));
|
||||
else
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("addednode", strAddNode));
|
||||
obj.push_back(Pair("connected", false));
|
||||
UniValue addresses(UniValue::VARR);
|
||||
obj.push_back(Pair("addresses", addresses));
|
||||
}
|
||||
}
|
||||
|
||||
LOCK(cs_vNodes);
|
||||
for (list<pair<string, vector<CService> > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++)
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("addednode", it->first));
|
||||
|
||||
UniValue addresses(UniValue::VARR);
|
||||
bool fConnected = false;
|
||||
BOOST_FOREACH(const CService& addrNode, it->second) {
|
||||
bool fFound = false;
|
||||
UniValue node(UniValue::VOBJ);
|
||||
node.push_back(Pair("address", addrNode.ToString()));
|
||||
BOOST_FOREACH(CNode* pnode, vNodes) {
|
||||
if (pnode->addr == addrNode)
|
||||
{
|
||||
fFound = true;
|
||||
fConnected = true;
|
||||
node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fFound)
|
||||
node.push_back(Pair("connected", "false"));
|
||||
addresses.push_back(node);
|
||||
}
|
||||
obj.push_back(Pair("connected", fConnected));
|
||||
obj.push_back(Pair("addresses", addresses));
|
||||
ret.push_back(obj);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue getnettotals(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 0)
|
||||
throw runtime_error(
|
||||
"getnettotals\n"
|
||||
"\nReturns information about network traffic, including bytes in, bytes out,\n"
|
||||
"and current time.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"totalbytesrecv\": n, (numeric) Total bytes received\n"
|
||||
" \"totalbytessent\": n, (numeric) Total bytes sent\n"
|
||||
" \"timemillis\": t (numeric) Total cpu time\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getnettotals", "")
|
||||
+ HelpExampleRpc("getnettotals", "")
|
||||
);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv()));
|
||||
obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent()));
|
||||
obj.push_back(Pair("timemillis", GetTimeMillis()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
static UniValue GetNetworksInfo()
|
||||
{
|
||||
UniValue networks(UniValue::VARR);
|
||||
for(int n=0; n<NET_MAX; ++n)
|
||||
{
|
||||
enum Network network = static_cast<enum Network>(n);
|
||||
if(network == NET_UNROUTABLE)
|
||||
continue;
|
||||
proxyType proxy;
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
GetProxy(network, proxy);
|
||||
obj.push_back(Pair("name", GetNetworkName(network)));
|
||||
obj.push_back(Pair("limited", IsLimited(network)));
|
||||
obj.push_back(Pair("reachable", IsReachable(network)));
|
||||
obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()));
|
||||
obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials));
|
||||
networks.push_back(obj);
|
||||
}
|
||||
return networks;
|
||||
}
|
||||
|
||||
UniValue getdeprecationinfo(const UniValue& params, bool fHelp)
|
||||
{
|
||||
const CChainParams& chainparams = Params();
|
||||
if (fHelp || params.size() != 0 || chainparams.NetworkIDString() != "main")
|
||||
throw runtime_error(
|
||||
"getdeprecationinfo\n"
|
||||
"Returns an object containing current version and deprecation block height. Applicable only on mainnet.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"version\": xxxxx, (numeric) the server version\n"
|
||||
" \"subversion\": \"/MagicBean:x.y.z[-v]/\", (string) the server subversion string\n"
|
||||
" \"deprecationheight\": xxxxx, (numeric) the block height at which this version will deprecate and shut down\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getdeprecationinfo", "")
|
||||
+ HelpExampleRpc("getdeprecationinfo", "")
|
||||
);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("version", CLIENT_VERSION));
|
||||
obj.push_back(Pair("subversion",
|
||||
FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>())));
|
||||
obj.push_back(Pair("deprecationheight", DEPRECATION_HEIGHT));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue getnetworkinfo(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getnetworkinfo\n"
|
||||
"Returns an object containing various state info regarding P2P networking.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"version\": xxxxx, (numeric) the server version\n"
|
||||
" \"subversion\": \"/MagicBean:x.y.z[-v]/\", (string) the server subversion string\n"
|
||||
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
|
||||
" \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n"
|
||||
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
|
||||
" \"connections\": xxxxx, (numeric) the number of connections\n"
|
||||
" \"networks\": [ (array) information per network\n"
|
||||
" {\n"
|
||||
" \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n"
|
||||
" \"limited\": true|false, (boolean) is the network limited using -onlynet?\n"
|
||||
" \"reachable\": true|false, (boolean) is the network reachable?\n"
|
||||
" \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n"
|
||||
" \"localaddresses\": [ (array) list of local addresses\n"
|
||||
" {\n"
|
||||
" \"address\": \"xxxx\", (string) network address\n"
|
||||
" \"port\": xxx, (numeric) network port\n"
|
||||
" \"score\": xxx (numeric) relative score\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" \"warnings\": \"...\" (string) any network warnings (such as alert messages) \n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getnetworkinfo", "")
|
||||
+ HelpExampleRpc("getnetworkinfo", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("version", CLIENT_VERSION));
|
||||
obj.push_back(Pair("subversion", strSubVersion));
|
||||
obj.push_back(Pair("protocolversion",PROTOCOL_VERSION));
|
||||
obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices)));
|
||||
obj.push_back(Pair("timeoffset", GetTimeOffset()));
|
||||
obj.push_back(Pair("connections", (int)vNodes.size()));
|
||||
obj.push_back(Pair("networks", GetNetworksInfo()));
|
||||
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
|
||||
UniValue localAddresses(UniValue::VARR);
|
||||
{
|
||||
LOCK(cs_mapLocalHost);
|
||||
BOOST_FOREACH(const PAIRTYPE(CNetAddr, LocalServiceInfo) &item, mapLocalHost)
|
||||
{
|
||||
UniValue rec(UniValue::VOBJ);
|
||||
rec.push_back(Pair("address", item.first.ToString()));
|
||||
rec.push_back(Pair("port", item.second.nPort));
|
||||
rec.push_back(Pair("score", item.second.nScore));
|
||||
localAddresses.push_back(rec);
|
||||
}
|
||||
}
|
||||
obj.push_back(Pair("localaddresses", localAddresses));
|
||||
obj.push_back(Pair("warnings", GetWarnings("statusbar")));
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue setban(const UniValue& params, bool fHelp)
|
||||
{
|
||||
string strCommand;
|
||||
if (params.size() >= 2)
|
||||
strCommand = params[1].get_str();
|
||||
if (fHelp || params.size() < 2 ||
|
||||
(strCommand != "add" && strCommand != "remove"))
|
||||
throw runtime_error(
|
||||
"setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n"
|
||||
"\nAttempts add or remove a IP/Subnet from the banned list.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"ip(/netmask)\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n"
|
||||
"2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n"
|
||||
"3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n"
|
||||
"4. \"absolute\" (boolean, optional) If set, the bantime must be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
|
||||
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
|
||||
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400")
|
||||
);
|
||||
|
||||
CSubNet subNet;
|
||||
CNetAddr netAddr;
|
||||
bool isSubnet = false;
|
||||
|
||||
if (params[0].get_str().find("/") != string::npos)
|
||||
isSubnet = true;
|
||||
|
||||
if (!isSubnet)
|
||||
netAddr = CNetAddr(params[0].get_str());
|
||||
else
|
||||
subNet = CSubNet(params[0].get_str());
|
||||
|
||||
if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet");
|
||||
|
||||
if (strCommand == "add")
|
||||
{
|
||||
if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr))
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
|
||||
|
||||
int64_t banTime = 0; //use standard bantime if not specified
|
||||
if (params.size() >= 3 && !params[2].isNull())
|
||||
banTime = params[2].get_int64();
|
||||
|
||||
bool absolute = false;
|
||||
if (params.size() == 4 && params[3].isTrue())
|
||||
absolute = true;
|
||||
|
||||
isSubnet ? CNode::Ban(subNet, banTime, absolute) : CNode::Ban(netAddr, banTime, absolute);
|
||||
|
||||
//disconnect possible nodes
|
||||
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
|
||||
bannedNode->fDisconnect = true;
|
||||
}
|
||||
else if(strCommand == "remove")
|
||||
{
|
||||
if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) ))
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed");
|
||||
}
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue listbanned(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"listbanned\n"
|
||||
"\nList all banned IPs/Subnets.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("listbanned", "")
|
||||
+ HelpExampleRpc("listbanned", "")
|
||||
);
|
||||
|
||||
std::map<CSubNet, int64_t> banMap;
|
||||
CNode::GetBanned(banMap);
|
||||
|
||||
UniValue bannedAddresses(UniValue::VARR);
|
||||
for (std::map<CSubNet, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++)
|
||||
{
|
||||
UniValue rec(UniValue::VOBJ);
|
||||
rec.push_back(Pair("address", (*it).first.ToString()));
|
||||
rec.push_back(Pair("banned_until", (*it).second));
|
||||
bannedAddresses.push_back(rec);
|
||||
}
|
||||
|
||||
return bannedAddresses;
|
||||
}
|
||||
|
||||
UniValue clearbanned(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"clearbanned\n"
|
||||
"\nClear all banned IPs.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("clearbanned", "")
|
||||
+ HelpExampleRpc("clearbanned", "")
|
||||
);
|
||||
|
||||
CNode::ClearBanned();
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
127
src/rpc/protocol.cpp
Normal file
127
src/rpc/protocol.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "rpc/protocol.h"
|
||||
|
||||
#include "random.h"
|
||||
#include "tinyformat.h"
|
||||
#include "util.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "utiltime.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
|
||||
* but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
|
||||
* unspecified (HTTP errors and contents of 'error').
|
||||
*
|
||||
* 1.0 spec: http://json-rpc.org/wiki/specification
|
||||
* 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
|
||||
*/
|
||||
|
||||
string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id)
|
||||
{
|
||||
UniValue request(UniValue::VOBJ);
|
||||
request.push_back(Pair("method", strMethod));
|
||||
request.push_back(Pair("params", params));
|
||||
request.push_back(Pair("id", id));
|
||||
return request.write() + "\n";
|
||||
}
|
||||
|
||||
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id)
|
||||
{
|
||||
UniValue reply(UniValue::VOBJ);
|
||||
if (!error.isNull())
|
||||
reply.push_back(Pair("result", NullUniValue));
|
||||
else
|
||||
reply.push_back(Pair("result", result));
|
||||
reply.push_back(Pair("error", error));
|
||||
reply.push_back(Pair("id", id));
|
||||
return reply;
|
||||
}
|
||||
|
||||
string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id)
|
||||
{
|
||||
UniValue reply = JSONRPCReplyObj(result, error, id);
|
||||
return reply.write() + "\n";
|
||||
}
|
||||
|
||||
UniValue JSONRPCError(int code, const string& message)
|
||||
{
|
||||
UniValue error(UniValue::VOBJ);
|
||||
error.push_back(Pair("code", code));
|
||||
error.push_back(Pair("message", message));
|
||||
return error;
|
||||
}
|
||||
|
||||
/** Username used when cookie authentication is in use (arbitrary, only for
|
||||
* recognizability in debugging/logging purposes)
|
||||
*/
|
||||
static const std::string COOKIEAUTH_USER = "__cookie__";
|
||||
/** Default name for auth cookie file */
|
||||
static const std::string COOKIEAUTH_FILE = ".cookie";
|
||||
|
||||
boost::filesystem::path GetAuthCookieFile()
|
||||
{
|
||||
boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
|
||||
if (!path.is_complete()) path = GetDataDir() / path;
|
||||
return path;
|
||||
}
|
||||
|
||||
bool GenerateAuthCookie(std::string *cookie_out)
|
||||
{
|
||||
unsigned char rand_pwd[32];
|
||||
GetRandBytes(rand_pwd, 32);
|
||||
std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32);
|
||||
|
||||
/** the umask determines what permissions are used to create this file -
|
||||
* these are set to 077 in init.cpp unless overridden with -sysperms.
|
||||
*/
|
||||
std::ofstream file;
|
||||
boost::filesystem::path filepath = GetAuthCookieFile();
|
||||
file.open(filepath.string().c_str());
|
||||
if (!file.is_open()) {
|
||||
LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
|
||||
return false;
|
||||
}
|
||||
file << cookie;
|
||||
file.close();
|
||||
LogPrintf("Generated RPC authentication cookie %s\n", filepath.string());
|
||||
|
||||
if (cookie_out)
|
||||
*cookie_out = cookie;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetAuthCookie(std::string *cookie_out)
|
||||
{
|
||||
std::ifstream file;
|
||||
std::string cookie;
|
||||
boost::filesystem::path filepath = GetAuthCookieFile();
|
||||
file.open(filepath.string().c_str());
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
std::getline(file, cookie);
|
||||
file.close();
|
||||
|
||||
if (cookie_out)
|
||||
*cookie_out = cookie;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeleteAuthCookie()
|
||||
{
|
||||
try {
|
||||
boost::filesystem::remove(GetAuthCookieFile());
|
||||
} catch (const boost::filesystem::filesystem_error& e) {
|
||||
LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
93
src/rpc/protocol.h
Normal file
93
src/rpc/protocol.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_RPCPROTOCOL_H
|
||||
#define BITCOIN_RPCPROTOCOL_H
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
//! HTTP status codes
|
||||
enum HTTPStatusCode
|
||||
{
|
||||
HTTP_OK = 200,
|
||||
HTTP_BAD_REQUEST = 400,
|
||||
HTTP_UNAUTHORIZED = 401,
|
||||
HTTP_FORBIDDEN = 403,
|
||||
HTTP_NOT_FOUND = 404,
|
||||
HTTP_BAD_METHOD = 405,
|
||||
HTTP_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_SERVICE_UNAVAILABLE = 503,
|
||||
};
|
||||
|
||||
//! Bitcoin RPC error codes
|
||||
enum RPCErrorCode
|
||||
{
|
||||
//! Standard JSON-RPC 2.0 errors
|
||||
RPC_INVALID_REQUEST = -32600,
|
||||
RPC_METHOD_NOT_FOUND = -32601,
|
||||
RPC_INVALID_PARAMS = -32602,
|
||||
RPC_INTERNAL_ERROR = -32603,
|
||||
RPC_PARSE_ERROR = -32700,
|
||||
|
||||
//! General application defined errors
|
||||
RPC_MISC_ERROR = -1, //! std::exception thrown in command handling
|
||||
RPC_FORBIDDEN_BY_SAFE_MODE = -2, //! Server is in safe mode, and command is not allowed in safe mode
|
||||
RPC_TYPE_ERROR = -3, //! Unexpected type was passed as parameter
|
||||
RPC_INVALID_ADDRESS_OR_KEY = -5, //! Invalid address or key
|
||||
RPC_OUT_OF_MEMORY = -7, //! Ran out of memory during operation
|
||||
RPC_INVALID_PARAMETER = -8, //! Invalid, missing or duplicate parameter
|
||||
RPC_DATABASE_ERROR = -20, //! Database error
|
||||
RPC_DESERIALIZATION_ERROR = -22, //! Error parsing or validating structure in raw format
|
||||
RPC_VERIFY_ERROR = -25, //! General error during transaction or block submission
|
||||
RPC_VERIFY_REJECTED = -26, //! Transaction or block was rejected by network rules
|
||||
RPC_VERIFY_ALREADY_IN_CHAIN = -27, //! Transaction already in chain
|
||||
RPC_IN_WARMUP = -28, //! Client still warming up
|
||||
|
||||
//! Aliases for backward compatibility
|
||||
RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR,
|
||||
RPC_TRANSACTION_REJECTED = RPC_VERIFY_REJECTED,
|
||||
RPC_TRANSACTION_ALREADY_IN_CHAIN= RPC_VERIFY_ALREADY_IN_CHAIN,
|
||||
|
||||
//! P2P client errors
|
||||
RPC_CLIENT_NOT_CONNECTED = -9, //! Bitcoin is not connected
|
||||
RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, //! Still downloading initial blocks
|
||||
RPC_CLIENT_NODE_ALREADY_ADDED = -23, //! Node is already added
|
||||
RPC_CLIENT_NODE_NOT_ADDED = -24, //! Node has not been added before
|
||||
RPC_CLIENT_NODE_NOT_CONNECTED = -29, //! Node to disconnect not found in connected nodes
|
||||
RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //! Invalid IP/Subnet
|
||||
|
||||
//! Wallet errors
|
||||
RPC_WALLET_ERROR = -4, //! Unspecified problem with wallet (key not found etc.)
|
||||
RPC_WALLET_INSUFFICIENT_FUNDS = -6, //! Not enough funds in wallet or account
|
||||
RPC_WALLET_ACCOUNTS_UNSUPPORTED = -11, //! Accounts are unsupported
|
||||
RPC_WALLET_KEYPOOL_RAN_OUT = -12, //! Keypool ran out, call keypoolrefill first
|
||||
RPC_WALLET_UNLOCK_NEEDED = -13, //! Enter the wallet passphrase with walletpassphrase first
|
||||
RPC_WALLET_PASSPHRASE_INCORRECT = -14, //! The wallet passphrase entered was incorrect
|
||||
RPC_WALLET_WRONG_ENC_STATE = -15, //! Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
|
||||
RPC_WALLET_ENCRYPTION_FAILED = -16, //! Failed to encrypt the wallet
|
||||
RPC_WALLET_ALREADY_UNLOCKED = -17, //! Wallet is already unlocked
|
||||
};
|
||||
|
||||
std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id);
|
||||
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
|
||||
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
|
||||
UniValue JSONRPCError(int code, const std::string& message);
|
||||
|
||||
/** Get name of RPC authentication cookie file */
|
||||
boost::filesystem::path GetAuthCookieFile();
|
||||
/** Generate a new RPC authentication cookie and write it to disk */
|
||||
bool GenerateAuthCookie(std::string *cookie_out);
|
||||
/** Read the RPC authentication cookie from disk */
|
||||
bool GetAuthCookie(std::string *cookie_out);
|
||||
/** Delete RPC authentication cookie from disk */
|
||||
void DeleteAuthCookie();
|
||||
|
||||
#endif // BITCOIN_RPCPROTOCOL_H
|
||||
978
src/rpc/rawtransaction.cpp
Normal file
978
src/rpc/rawtransaction.cpp
Normal file
@@ -0,0 +1,978 @@
|
||||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "consensus/upgrades.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "core_io.h"
|
||||
#include "init.h"
|
||||
#include "key_io.h"
|
||||
#include "keystore.h"
|
||||
#include "main.h"
|
||||
#include "merkleblock.h"
|
||||
#include "net.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "rpc/server.h"
|
||||
#include "script/script.h"
|
||||
#include "script/script_error.h"
|
||||
#include "script/sign.h"
|
||||
#include "script/standard.h"
|
||||
#include "uint256.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "wallet/wallet.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
|
||||
{
|
||||
txnouttype type;
|
||||
vector<CTxDestination> addresses;
|
||||
int nRequired;
|
||||
|
||||
out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey)));
|
||||
if (fIncludeHex)
|
||||
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
||||
|
||||
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
|
||||
out.push_back(Pair("type", GetTxnOutputType(type)));
|
||||
return;
|
||||
}
|
||||
|
||||
out.push_back(Pair("reqSigs", nRequired));
|
||||
out.push_back(Pair("type", GetTxnOutputType(type)));
|
||||
|
||||
UniValue a(UniValue::VARR);
|
||||
for (const CTxDestination& addr : addresses) {
|
||||
a.push_back(EncodeDestination(addr));
|
||||
}
|
||||
out.push_back(Pair("addresses", a));
|
||||
}
|
||||
|
||||
|
||||
UniValue TxJoinSplitToJSON(const CTransaction& tx) {
|
||||
bool useGroth = tx.fOverwintered && tx.nVersion >= SAPLING_TX_VERSION;
|
||||
UniValue vjoinsplit(UniValue::VARR);
|
||||
for (unsigned int i = 0; i < tx.vjoinsplit.size(); i++) {
|
||||
const JSDescription& jsdescription = tx.vjoinsplit[i];
|
||||
UniValue joinsplit(UniValue::VOBJ);
|
||||
|
||||
joinsplit.push_back(Pair("vpub_old", ValueFromAmount(jsdescription.vpub_old)));
|
||||
joinsplit.push_back(Pair("vpub_new", ValueFromAmount(jsdescription.vpub_new)));
|
||||
|
||||
joinsplit.push_back(Pair("anchor", jsdescription.anchor.GetHex()));
|
||||
|
||||
{
|
||||
UniValue nullifiers(UniValue::VARR);
|
||||
BOOST_FOREACH(const uint256 nf, jsdescription.nullifiers) {
|
||||
nullifiers.push_back(nf.GetHex());
|
||||
}
|
||||
joinsplit.push_back(Pair("nullifiers", nullifiers));
|
||||
}
|
||||
|
||||
{
|
||||
UniValue commitments(UniValue::VARR);
|
||||
BOOST_FOREACH(const uint256 commitment, jsdescription.commitments) {
|
||||
commitments.push_back(commitment.GetHex());
|
||||
}
|
||||
joinsplit.push_back(Pair("commitments", commitments));
|
||||
}
|
||||
|
||||
joinsplit.push_back(Pair("onetimePubKey", jsdescription.ephemeralKey.GetHex()));
|
||||
joinsplit.push_back(Pair("randomSeed", jsdescription.randomSeed.GetHex()));
|
||||
|
||||
{
|
||||
UniValue macs(UniValue::VARR);
|
||||
BOOST_FOREACH(const uint256 mac, jsdescription.macs) {
|
||||
macs.push_back(mac.GetHex());
|
||||
}
|
||||
joinsplit.push_back(Pair("macs", macs));
|
||||
}
|
||||
|
||||
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
||||
auto ps = SproutProofSerializer<CDataStream>(ssProof, useGroth);
|
||||
boost::apply_visitor(ps, jsdescription.proof);
|
||||
joinsplit.push_back(Pair("proof", HexStr(ssProof.begin(), ssProof.end())));
|
||||
|
||||
{
|
||||
UniValue ciphertexts(UniValue::VARR);
|
||||
for (const ZCNoteEncryption::Ciphertext ct : jsdescription.ciphertexts) {
|
||||
ciphertexts.push_back(HexStr(ct.begin(), ct.end()));
|
||||
}
|
||||
joinsplit.push_back(Pair("ciphertexts", ciphertexts));
|
||||
}
|
||||
|
||||
vjoinsplit.push_back(joinsplit);
|
||||
}
|
||||
return vjoinsplit;
|
||||
}
|
||||
|
||||
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
||||
{
|
||||
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
|
||||
entry.push_back(Pair("overwintered", tx.fOverwintered));
|
||||
entry.push_back(Pair("version", tx.nVersion));
|
||||
if (tx.fOverwintered) {
|
||||
entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId)));
|
||||
}
|
||||
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
|
||||
if (tx.fOverwintered) {
|
||||
entry.push_back(Pair("expiryheight", (int64_t)tx.nExpiryHeight));
|
||||
}
|
||||
UniValue vin(UniValue::VARR);
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||
UniValue in(UniValue::VOBJ);
|
||||
if (tx.IsCoinBase())
|
||||
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||
else {
|
||||
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
|
||||
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
|
||||
UniValue o(UniValue::VOBJ);
|
||||
o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
|
||||
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||
in.push_back(Pair("scriptSig", o));
|
||||
}
|
||||
in.push_back(Pair("sequence", (int64_t)txin.nSequence));
|
||||
vin.push_back(in);
|
||||
}
|
||||
entry.push_back(Pair("vin", vin));
|
||||
UniValue vout(UniValue::VARR);
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
const CTxOut& txout = tx.vout[i];
|
||||
UniValue out(UniValue::VOBJ);
|
||||
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
|
||||
out.push_back(Pair("valueZat", txout.nValue));
|
||||
out.push_back(Pair("n", (int64_t)i));
|
||||
UniValue o(UniValue::VOBJ);
|
||||
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
|
||||
out.push_back(Pair("scriptPubKey", o));
|
||||
vout.push_back(out);
|
||||
}
|
||||
entry.push_back(Pair("vout", vout));
|
||||
|
||||
UniValue vjoinsplit = TxJoinSplitToJSON(tx);
|
||||
entry.push_back(Pair("vjoinsplit", vjoinsplit));
|
||||
|
||||
if (!hashBlock.IsNull()) {
|
||||
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
|
||||
if (mi != mapBlockIndex.end() && (*mi).second) {
|
||||
CBlockIndex* pindex = (*mi).second;
|
||||
if (chainActive.Contains(pindex)) {
|
||||
entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
|
||||
entry.push_back(Pair("time", pindex->GetBlockTime()));
|
||||
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
|
||||
}
|
||||
else
|
||||
entry.push_back(Pair("confirmations", 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UniValue getrawtransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"getrawtransaction \"txid\" ( verbose )\n"
|
||||
"\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n"
|
||||
"or there is an unspent output in the utxo for this transaction. To make it always work,\n"
|
||||
"you need to maintain a transaction index, using the -txindex command line option.\n"
|
||||
"\nReturn the raw transaction data.\n"
|
||||
"\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n"
|
||||
"If verbose is non-zero, returns an Object with information about 'txid'.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"txid\" (string, required) The transaction id\n"
|
||||
"2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n"
|
||||
|
||||
"\nResult (if verbose is not set or set to 0):\n"
|
||||
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
|
||||
|
||||
"\nResult (if verbose > 0):\n"
|
||||
"{\n"
|
||||
" \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
|
||||
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
|
||||
" \"version\" : n, (numeric) The version\n"
|
||||
" \"locktime\" : ttt, (numeric) The lock time\n"
|
||||
" \"expiryheight\" : ttt, (numeric, optional) The block height after which the transaction expires\n"
|
||||
" \"vin\" : [ (array of json objects)\n"
|
||||
" {\n"
|
||||
" \"txid\": \"id\", (string) The transaction id\n"
|
||||
" \"vout\": n, (numeric) \n"
|
||||
" \"scriptSig\": { (json object) The script\n"
|
||||
" \"asm\": \"asm\", (string) asm\n"
|
||||
" \"hex\": \"hex\" (string) hex\n"
|
||||
" },\n"
|
||||
" \"sequence\": n (numeric) The script sequence number\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"vout\" : [ (array of json objects)\n"
|
||||
" {\n"
|
||||
" \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
|
||||
" \"n\" : n, (numeric) index\n"
|
||||
" \"scriptPubKey\" : { (json object)\n"
|
||||
" \"asm\" : \"asm\", (string) the asm\n"
|
||||
" \"hex\" : \"hex\", (string) the hex\n"
|
||||
" \"reqSigs\" : n, (numeric) The required sigs\n"
|
||||
" \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
|
||||
" \"addresses\" : [ (json array of string)\n"
|
||||
" \"zcashaddress\" (string) Zcash address\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"vjoinsplit\" : [ (array of json objects, only for version >= 2)\n"
|
||||
" {\n"
|
||||
" \"vpub_old\" : x.xxx, (numeric) public input value in " + CURRENCY_UNIT + "\n"
|
||||
" \"vpub_new\" : x.xxx, (numeric) public output value in " + CURRENCY_UNIT + "\n"
|
||||
" \"anchor\" : \"hex\", (string) the anchor\n"
|
||||
" \"nullifiers\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) input note nullifier\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"commitments\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) output note commitment\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"onetimePubKey\" : \"hex\", (string) the onetime public key used to encrypt the ciphertexts\n"
|
||||
" \"randomSeed\" : \"hex\", (string) the random seed\n"
|
||||
" \"macs\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) input note MAC\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"proof\" : \"hex\", (string) the zero-knowledge proof\n"
|
||||
" \"ciphertexts\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) output note ciphertext\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"blockhash\" : \"hash\", (string) the block hash\n"
|
||||
" \"confirmations\" : n, (numeric) The confirmations\n"
|
||||
" \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
" \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
"}\n"
|
||||
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getrawtransaction", "\"mytxid\"")
|
||||
+ HelpExampleCli("getrawtransaction", "\"mytxid\" 1")
|
||||
+ HelpExampleRpc("getrawtransaction", "\"mytxid\", 1")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
uint256 hash = ParseHashV(params[0], "parameter 1");
|
||||
|
||||
bool fVerbose = false;
|
||||
if (params.size() > 1)
|
||||
fVerbose = (params[1].get_int() != 0);
|
||||
|
||||
CTransaction tx;
|
||||
uint256 hashBlock;
|
||||
if (!GetTransaction(hash, tx, hashBlock, true))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
|
||||
|
||||
string strHex = EncodeHexTx(tx);
|
||||
|
||||
if (!fVerbose)
|
||||
return strHex;
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("hex", strHex));
|
||||
TxToJSON(tx, hashBlock, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
UniValue gettxoutproof(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || (params.size() != 1 && params.size() != 2))
|
||||
throw runtime_error(
|
||||
"gettxoutproof [\"txid\",...] ( blockhash )\n"
|
||||
"\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
|
||||
"\nNOTE: By default this function only works sometimes. This is when there is an\n"
|
||||
"unspent output in the utxo for this transaction. To make it always work,\n"
|
||||
"you need to maintain a transaction index, using the -txindex command line option or\n"
|
||||
"specify the block in which the transaction is included in manually (by blockhash).\n"
|
||||
"\nReturn the raw transaction data.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"txids\" (string) A json array of txids to filter\n"
|
||||
" [\n"
|
||||
" \"txid\" (string) A transaction hash\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"2. \"block hash\" (string, optional) If specified, looks for txid in the block with this hash\n"
|
||||
"\nResult:\n"
|
||||
"\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n"
|
||||
);
|
||||
|
||||
set<uint256> setTxids;
|
||||
uint256 oneTxid;
|
||||
UniValue txids = params[0].get_array();
|
||||
for (size_t idx = 0; idx < txids.size(); idx++) {
|
||||
const UniValue& txid = txids[idx];
|
||||
if (txid.get_str().length() != 64 || !IsHex(txid.get_str()))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str());
|
||||
uint256 hash(uint256S(txid.get_str()));
|
||||
if (setTxids.count(hash))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str());
|
||||
setTxids.insert(hash);
|
||||
oneTxid = hash;
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
CBlockIndex* pblockindex = NULL;
|
||||
|
||||
uint256 hashBlock;
|
||||
if (params.size() > 1)
|
||||
{
|
||||
hashBlock = uint256S(params[1].get_str());
|
||||
if (!mapBlockIndex.count(hashBlock))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
pblockindex = mapBlockIndex[hashBlock];
|
||||
} else {
|
||||
CCoins coins;
|
||||
if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height())
|
||||
pblockindex = chainActive[coins.nHeight];
|
||||
}
|
||||
|
||||
if (pblockindex == NULL)
|
||||
{
|
||||
CTransaction tx;
|
||||
if (!GetTransaction(oneTxid, tx, hashBlock, false) || hashBlock.IsNull())
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
|
||||
if (!mapBlockIndex.count(hashBlock))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
|
||||
pblockindex = mapBlockIndex[hashBlock];
|
||||
}
|
||||
|
||||
CBlock block;
|
||||
if(!ReadBlockFromDisk(block, pblockindex))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
|
||||
|
||||
unsigned int ntxFound = 0;
|
||||
BOOST_FOREACH(const CTransaction&tx, block.vtx)
|
||||
if (setTxids.count(tx.GetHash()))
|
||||
ntxFound++;
|
||||
if (ntxFound != setTxids.size())
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block");
|
||||
|
||||
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION);
|
||||
CMerkleBlock mb(block, setTxids);
|
||||
ssMB << mb;
|
||||
std::string strHex = HexStr(ssMB.begin(), ssMB.end());
|
||||
return strHex;
|
||||
}
|
||||
|
||||
UniValue verifytxoutproof(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"verifytxoutproof \"proof\"\n"
|
||||
"\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
|
||||
"and throwing an RPC error if the block is not in our best chain\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n"
|
||||
"\nResult:\n"
|
||||
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n"
|
||||
);
|
||||
|
||||
CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
CMerkleBlock merkleBlock;
|
||||
ssMB >> merkleBlock;
|
||||
|
||||
UniValue res(UniValue::VARR);
|
||||
|
||||
vector<uint256> vMatch;
|
||||
if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot)
|
||||
return res;
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()]))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
|
||||
|
||||
BOOST_FOREACH(const uint256& hash, vMatch)
|
||||
res.push_back(hash.GetHex());
|
||||
return res;
|
||||
}
|
||||
|
||||
UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 2 || params.size() > 3)
|
||||
throw runtime_error(
|
||||
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...} ( locktime )\n"
|
||||
"\nCreate a transaction spending the given inputs and sending to the given addresses.\n"
|
||||
"Returns hex-encoded raw transaction.\n"
|
||||
"Note that the transaction's inputs are not signed, and\n"
|
||||
"it is not stored in the wallet or transmitted to the network.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"transactions\" (string, required) A json array of json objects\n"
|
||||
" [\n"
|
||||
" {\n"
|
||||
" \"txid\":\"id\", (string, required) The transaction id\n"
|
||||
" \"vout\":n (numeric, required) The output number\n"
|
||||
" \"sequence\":n (numeric, optional) The sequence number\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n"
|
||||
" {\n"
|
||||
" \"address\": x.xxx (numeric, required) The key is the Zcash address, the value is the " + CURRENCY_UNIT + " amount\n"
|
||||
" ,...\n"
|
||||
" }\n"
|
||||
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
|
||||
"\nResult:\n"
|
||||
"\"transaction\" (string) hex string of the transaction\n"
|
||||
|
||||
"\nExamples\n"
|
||||
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"")
|
||||
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true);
|
||||
if (params[0].isNull() || params[1].isNull())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
|
||||
|
||||
UniValue inputs = params[0].get_array();
|
||||
UniValue sendTo = params[1].get_obj();
|
||||
|
||||
int nextBlockHeight = chainActive.Height() + 1;
|
||||
CMutableTransaction rawTx = CreateNewContextualCMutableTransaction(
|
||||
Params().GetConsensus(), nextBlockHeight);
|
||||
|
||||
if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
|
||||
if (rawTx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD){
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD.");
|
||||
}
|
||||
}
|
||||
|
||||
if (params.size() > 2 && !params[2].isNull()) {
|
||||
int64_t nLockTime = params[2].get_int64();
|
||||
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
|
||||
rawTx.nLockTime = nLockTime;
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < inputs.size(); idx++) {
|
||||
const UniValue& input = inputs[idx];
|
||||
const UniValue& o = input.get_obj();
|
||||
|
||||
uint256 txid = ParseHashO(o, "txid");
|
||||
|
||||
const UniValue& vout_v = find_value(o, "vout");
|
||||
if (!vout_v.isNum())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
|
||||
int nOutput = vout_v.get_int();
|
||||
if (nOutput < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
|
||||
|
||||
uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max());
|
||||
|
||||
// set the sequence number if passed in the parameters object
|
||||
const UniValue& sequenceObj = find_value(o, "sequence");
|
||||
if (sequenceObj.isNum())
|
||||
nSequence = sequenceObj.get_int();
|
||||
|
||||
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
|
||||
|
||||
rawTx.vin.push_back(in);
|
||||
}
|
||||
|
||||
std::set<CTxDestination> destinations;
|
||||
vector<string> addrList = sendTo.getKeys();
|
||||
for (const std::string& name_ : addrList) {
|
||||
CTxDestination destination = DecodeDestination(name_);
|
||||
if (!IsValidDestination(destination)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_);
|
||||
}
|
||||
|
||||
if (!destinations.insert(destination).second) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
|
||||
}
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(destination);
|
||||
CAmount nAmount = AmountFromValue(sendTo[name_]);
|
||||
|
||||
CTxOut out(nAmount, scriptPubKey);
|
||||
rawTx.vout.push_back(out);
|
||||
}
|
||||
|
||||
return EncodeHexTx(rawTx);
|
||||
}
|
||||
|
||||
UniValue decoderawtransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"decoderawtransaction \"hexstring\"\n"
|
||||
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"hex\" (string, required) The transaction hex string\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"txid\" : \"id\", (string) The transaction id\n"
|
||||
" \"overwintered\" : bool (boolean) The Overwintered flag\n"
|
||||
" \"version\" : n, (numeric) The version\n"
|
||||
" \"versiongroupid\": \"hex\" (string, optional) The version group id (Overwintered txs)\n"
|
||||
" \"locktime\" : ttt, (numeric) The lock time\n"
|
||||
" \"expiryheight\" : n, (numeric, optional) Last valid block height for mining transaction (Overwintered txs)\n"
|
||||
" \"vin\" : [ (array of json objects)\n"
|
||||
" {\n"
|
||||
" \"txid\": \"id\", (string) The transaction id\n"
|
||||
" \"vout\": n, (numeric) The output number\n"
|
||||
" \"scriptSig\": { (json object) The script\n"
|
||||
" \"asm\": \"asm\", (string) asm\n"
|
||||
" \"hex\": \"hex\" (string) hex\n"
|
||||
" },\n"
|
||||
" \"sequence\": n (numeric) The script sequence number\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"vout\" : [ (array of json objects)\n"
|
||||
" {\n"
|
||||
" \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
|
||||
" \"n\" : n, (numeric) index\n"
|
||||
" \"scriptPubKey\" : { (json object)\n"
|
||||
" \"asm\" : \"asm\", (string) the asm\n"
|
||||
" \"hex\" : \"hex\", (string) the hex\n"
|
||||
" \"reqSigs\" : n, (numeric) The required sigs\n"
|
||||
" \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
|
||||
" \"addresses\" : [ (json array of string)\n"
|
||||
" \"t12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) zcash address\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"vjoinsplit\" : [ (array of json objects, only for version >= 2)\n"
|
||||
" {\n"
|
||||
" \"vpub_old\" : x.xxx, (numeric) public input value in " + CURRENCY_UNIT + "\n"
|
||||
" \"vpub_new\" : x.xxx, (numeric) public output value in " + CURRENCY_UNIT + "\n"
|
||||
" \"anchor\" : \"hex\", (string) the anchor\n"
|
||||
" \"nullifiers\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) input note nullifier\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"commitments\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) output note commitment\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"onetimePubKey\" : \"hex\", (string) the onetime public key used to encrypt the ciphertexts\n"
|
||||
" \"randomSeed\" : \"hex\", (string) the random seed\n"
|
||||
" \"macs\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) input note MAC\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"proof\" : \"hex\", (string) the zero-knowledge proof\n"
|
||||
" \"ciphertexts\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) output note ciphertext\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
"}\n"
|
||||
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("decoderawtransaction", "\"hexstring\"")
|
||||
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
|
||||
|
||||
CTransaction tx;
|
||||
|
||||
if (!DecodeHexTx(tx, params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
TxToJSON(tx, uint256(), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UniValue decodescript(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"decodescript \"hex\"\n"
|
||||
"\nDecode a hex-encoded script.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"hex\" (string) the hex encoded script\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"asm\":\"asm\", (string) Script public key\n"
|
||||
" \"hex\":\"hex\", (string) hex encoded public key\n"
|
||||
" \"type\":\"type\", (string) The output type\n"
|
||||
" \"reqSigs\": n, (numeric) The required signatures\n"
|
||||
" \"addresses\": [ (json array of string)\n"
|
||||
" \"address\" (string) Zcash address\n"
|
||||
" ,...\n"
|
||||
" ],\n"
|
||||
" \"p2sh\",\"address\" (string) script address\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("decodescript", "\"hexstring\"")
|
||||
+ HelpExampleRpc("decodescript", "\"hexstring\"")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
|
||||
|
||||
UniValue r(UniValue::VOBJ);
|
||||
CScript script;
|
||||
if (params[0].get_str().size() > 0){
|
||||
vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
|
||||
script = CScript(scriptData.begin(), scriptData.end());
|
||||
} else {
|
||||
// Empty scripts are valid
|
||||
}
|
||||
ScriptPubKeyToJSON(script, r, false);
|
||||
|
||||
r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script))));
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
|
||||
static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
|
||||
{
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.push_back(Pair("txid", txin.prevout.hash.ToString()));
|
||||
entry.push_back(Pair("vout", (uint64_t)txin.prevout.n));
|
||||
entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||
entry.push_back(Pair("sequence", (uint64_t)txin.nSequence));
|
||||
entry.push_back(Pair("error", strMessage));
|
||||
vErrorsRet.push_back(entry);
|
||||
}
|
||||
|
||||
UniValue signrawtransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 4)
|
||||
throw runtime_error(
|
||||
"signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
|
||||
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
|
||||
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
|
||||
"this transaction depends on but may not yet be in the block chain.\n"
|
||||
"The third optional argument (may be null) is an array of base58-encoded private\n"
|
||||
"keys that, if given, will be the only keys used to sign the transaction.\n"
|
||||
#ifdef ENABLE_WALLET
|
||||
+ HelpRequiringPassphrase() + "\n"
|
||||
#endif
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"hexstring\" (string, required) The transaction hex string\n"
|
||||
"2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
|
||||
" [ (json array of json objects, or 'null' if none provided)\n"
|
||||
" {\n"
|
||||
" \"txid\":\"id\", (string, required) The transaction id\n"
|
||||
" \"vout\":n, (numeric, required) The output number\n"
|
||||
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
|
||||
" \"redeemScript\": \"hex\", (string, required for P2SH) redeem script\n"
|
||||
" \"amount\": value (numeric, required) The amount spent\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n"
|
||||
" [ (json array of strings, or 'null' if none provided)\n"
|
||||
" \"privatekey\" (string) private key in base58-encoding\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
|
||||
" \"ALL\"\n"
|
||||
" \"NONE\"\n"
|
||||
" \"SINGLE\"\n"
|
||||
" \"ALL|ANYONECANPAY\"\n"
|
||||
" \"NONE|ANYONECANPAY\"\n"
|
||||
" \"SINGLE|ANYONECANPAY\"\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
|
||||
" \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
|
||||
" \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
|
||||
" {\n"
|
||||
" \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
|
||||
" \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
|
||||
" \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
|
||||
" \"sequence\" : n, (numeric) Script sequence number\n"
|
||||
" \"error\" : \"text\" (string) Verification or signing error related to the input\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"}\n"
|
||||
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("signrawtransaction", "\"myhex\"")
|
||||
+ HelpExampleRpc("signrawtransaction", "\"myhex\"")
|
||||
);
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
|
||||
#else
|
||||
LOCK(cs_main);
|
||||
#endif
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true);
|
||||
|
||||
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
vector<CMutableTransaction> txVariants;
|
||||
while (!ssData.empty()) {
|
||||
try {
|
||||
CMutableTransaction tx;
|
||||
ssData >> tx;
|
||||
txVariants.push_back(tx);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (txVariants.empty())
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
|
||||
|
||||
// mergedTx will end up with all the signatures; it
|
||||
// starts as a clone of the rawtx:
|
||||
CMutableTransaction mergedTx(txVariants[0]);
|
||||
|
||||
// Fetch previous transactions (inputs):
|
||||
CCoinsView viewDummy;
|
||||
CCoinsViewCache view(&viewDummy);
|
||||
{
|
||||
LOCK(mempool.cs);
|
||||
CCoinsViewCache &viewChain = *pcoinsTip;
|
||||
CCoinsViewMemPool viewMempool(&viewChain, mempool);
|
||||
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
|
||||
|
||||
BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
|
||||
const uint256& prevHash = txin.prevout.hash;
|
||||
CCoins coins;
|
||||
view.AccessCoins(prevHash); // this is certainly allowed to fail
|
||||
}
|
||||
|
||||
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
|
||||
}
|
||||
|
||||
bool fGivenKeys = false;
|
||||
CBasicKeyStore tempKeystore;
|
||||
if (params.size() > 2 && !params[2].isNull()) {
|
||||
fGivenKeys = true;
|
||||
UniValue keys = params[2].get_array();
|
||||
for (size_t idx = 0; idx < keys.size(); idx++) {
|
||||
UniValue k = keys[idx];
|
||||
CKey key = DecodeSecret(k.get_str());
|
||||
if (!key.IsValid())
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
|
||||
tempKeystore.AddKey(key);
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_WALLET
|
||||
else if (pwalletMain)
|
||||
EnsureWalletIsUnlocked();
|
||||
#endif
|
||||
|
||||
// Add previous txouts given in the RPC call:
|
||||
if (params.size() > 1 && !params[1].isNull()) {
|
||||
UniValue prevTxs = params[1].get_array();
|
||||
for (size_t idx = 0; idx < prevTxs.size(); idx++) {
|
||||
const UniValue& p = prevTxs[idx];
|
||||
if (!p.isObject())
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
|
||||
|
||||
UniValue prevOut = p.get_obj();
|
||||
|
||||
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR));
|
||||
|
||||
uint256 txid = ParseHashO(prevOut, "txid");
|
||||
|
||||
int nOut = find_value(prevOut, "vout").get_int();
|
||||
if (nOut < 0)
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
|
||||
|
||||
vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
|
||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||
|
||||
{
|
||||
CCoinsModifier coins = view.ModifyCoins(txid);
|
||||
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
||||
string err("Previous output scriptPubKey mismatch:\n");
|
||||
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
|
||||
ScriptToAsmStr(scriptPubKey);
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
|
||||
}
|
||||
if ((unsigned int)nOut >= coins->vout.size())
|
||||
coins->vout.resize(nOut+1);
|
||||
coins->vout[nOut].scriptPubKey = scriptPubKey;
|
||||
coins->vout[nOut].nValue = 0;
|
||||
if (prevOut.exists("amount")) {
|
||||
coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount"));
|
||||
}
|
||||
}
|
||||
|
||||
// if redeemScript given and not using the local wallet (private keys
|
||||
// given), add redeemScript to the tempKeystore so it can be signed:
|
||||
if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) {
|
||||
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR));
|
||||
UniValue v = find_value(prevOut, "redeemScript");
|
||||
if (!v.isNull()) {
|
||||
vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
|
||||
CScript redeemScript(rsData.begin(), rsData.end());
|
||||
tempKeystore.AddCScript(redeemScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain);
|
||||
#else
|
||||
const CKeyStore& keystore = tempKeystore;
|
||||
#endif
|
||||
|
||||
int nHashType = SIGHASH_ALL;
|
||||
if (params.size() > 3 && !params[3].isNull()) {
|
||||
static map<string, int> mapSigHashValues =
|
||||
boost::assign::map_list_of
|
||||
(string("ALL"), int(SIGHASH_ALL))
|
||||
(string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
|
||||
(string("NONE"), int(SIGHASH_NONE))
|
||||
(string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
|
||||
(string("SINGLE"), int(SIGHASH_SINGLE))
|
||||
(string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
|
||||
;
|
||||
string strHashType = params[3].get_str();
|
||||
if (mapSigHashValues.count(strHashType))
|
||||
nHashType = mapSigHashValues[strHashType];
|
||||
else
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
|
||||
}
|
||||
|
||||
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
||||
|
||||
// Grab the current consensus branch ID
|
||||
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
|
||||
|
||||
// Script verification errors
|
||||
UniValue vErrors(UniValue::VARR);
|
||||
|
||||
// Use CTransaction for the constant parts of the
|
||||
// transaction to avoid rehashing.
|
||||
const CTransaction txConst(mergedTx);
|
||||
// Sign what we can:
|
||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
||||
CTxIn& txin = mergedTx.vin[i];
|
||||
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
|
||||
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
|
||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
|
||||
continue;
|
||||
}
|
||||
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
|
||||
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
|
||||
|
||||
SignatureData sigdata;
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
if (!fHashSingle || (i < mergedTx.vout.size()))
|
||||
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, consensusBranchId);
|
||||
|
||||
// ... and merge in other signatures:
|
||||
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
|
||||
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId);
|
||||
}
|
||||
|
||||
UpdateTransaction(mergedTx, i, sigdata);
|
||||
|
||||
ScriptError serror = SCRIPT_ERR_OK;
|
||||
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), consensusBranchId, &serror)) {
|
||||
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
|
||||
}
|
||||
}
|
||||
bool fComplete = vErrors.empty();
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
|
||||
result.push_back(Pair("complete", fComplete));
|
||||
if (!vErrors.empty()) {
|
||||
result.push_back(Pair("errors", vErrors));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UniValue sendrawtransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"sendrawtransaction \"hexstring\" ( allowhighfees )\n"
|
||||
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
|
||||
"\nAlso see createrawtransaction and signrawtransaction calls.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"hexstring\" (string, required) The hex string of the raw transaction)\n"
|
||||
"2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
|
||||
"\nResult:\n"
|
||||
"\"hex\" (string) The transaction hash in hex\n"
|
||||
"\nExamples:\n"
|
||||
"\nCreate a transaction\n"
|
||||
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
|
||||
"Sign the transaction, and get back the hex\n"
|
||||
+ HelpExampleCli("signrawtransaction", "\"myhex\"") +
|
||||
"\nSend the transaction (signed hex)\n"
|
||||
+ HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
|
||||
"\nAs a json rpc call\n"
|
||||
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
|
||||
|
||||
// parse hex string from parameter
|
||||
CTransaction tx;
|
||||
if (!DecodeHexTx(tx, params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
uint256 hashTx = tx.GetHash();
|
||||
|
||||
bool fOverrideFees = false;
|
||||
if (params.size() > 1)
|
||||
fOverrideFees = params[1].get_bool();
|
||||
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
const CCoins* existingCoins = view.AccessCoins(hashTx);
|
||||
bool fHaveMempool = mempool.exists(hashTx);
|
||||
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
|
||||
if (!fHaveMempool && !fHaveChain) {
|
||||
// push to local node and sync with wallets
|
||||
CValidationState state;
|
||||
bool fMissingInputs;
|
||||
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) {
|
||||
if (state.IsInvalid()) {
|
||||
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
|
||||
} else {
|
||||
if (fMissingInputs) {
|
||||
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
|
||||
}
|
||||
throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
|
||||
}
|
||||
}
|
||||
} else if (fHaveChain) {
|
||||
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
|
||||
}
|
||||
RelayTransaction(tx);
|
||||
|
||||
return hashTx.GetHex();
|
||||
}
|
||||
578
src/rpc/server.cpp
Normal file
578
src/rpc/server.cpp
Normal file
@@ -0,0 +1,578 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "rpc/server.h"
|
||||
|
||||
#include "init.h"
|
||||
#include "key_io.h"
|
||||
#include "random.h"
|
||||
#include "sync.h"
|
||||
#include "ui_interface.h"
|
||||
#include "util.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "asyncrpcqueue.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp> // for to_upper()
|
||||
|
||||
using namespace RPCServer;
|
||||
using namespace std;
|
||||
|
||||
static bool fRPCRunning = false;
|
||||
static bool fRPCInWarmup = true;
|
||||
static std::string rpcWarmupStatus("RPC server started");
|
||||
static CCriticalSection cs_rpcWarmup;
|
||||
/* Timer-creating functions */
|
||||
static std::vector<RPCTimerInterface*> timerInterfaces;
|
||||
/* Map of name to timer.
|
||||
* @note Can be changed to std::unique_ptr when C++11 */
|
||||
static std::map<std::string, boost::shared_ptr<RPCTimerBase> > deadlineTimers;
|
||||
|
||||
static struct CRPCSignals
|
||||
{
|
||||
boost::signals2::signal<void ()> Started;
|
||||
boost::signals2::signal<void ()> Stopped;
|
||||
boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
|
||||
boost::signals2::signal<void (const CRPCCommand&)> PostCommand;
|
||||
} g_rpcSignals;
|
||||
|
||||
void RPCServer::OnStarted(boost::function<void ()> slot)
|
||||
{
|
||||
g_rpcSignals.Started.connect(slot);
|
||||
}
|
||||
|
||||
void RPCServer::OnStopped(boost::function<void ()> slot)
|
||||
{
|
||||
g_rpcSignals.Stopped.connect(slot);
|
||||
}
|
||||
|
||||
void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot)
|
||||
{
|
||||
g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
|
||||
}
|
||||
|
||||
void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot)
|
||||
{
|
||||
g_rpcSignals.PostCommand.connect(boost::bind(slot, _1));
|
||||
}
|
||||
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const list<UniValue::VType>& typesExpected,
|
||||
bool fAllowNull)
|
||||
{
|
||||
size_t i = 0;
|
||||
BOOST_FOREACH(UniValue::VType t, typesExpected)
|
||||
{
|
||||
if (params.size() <= i)
|
||||
break;
|
||||
|
||||
const UniValue& v = params[i];
|
||||
if (!((v.type() == t) || (fAllowNull && (v.isNull()))))
|
||||
{
|
||||
string err = strprintf("Expected type %s, got %s",
|
||||
uvTypeName(t), uvTypeName(v.type()));
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, err);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void RPCTypeCheckObj(const UniValue& o,
|
||||
const map<string, UniValue::VType>& typesExpected,
|
||||
bool fAllowNull)
|
||||
{
|
||||
BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected)
|
||||
{
|
||||
const UniValue& v = find_value(o, t.first);
|
||||
if (!fAllowNull && v.isNull())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
|
||||
|
||||
if (!((v.type() == t.second) || (fAllowNull && (v.isNull()))))
|
||||
{
|
||||
string err = strprintf("Expected type %s for %s, got %s",
|
||||
uvTypeName(t.second), t.first, uvTypeName(v.type()));
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAmount AmountFromValue(const UniValue& value)
|
||||
{
|
||||
if (!value.isNum() && !value.isStr())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
|
||||
CAmount amount;
|
||||
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
|
||||
if (!MoneyRange(amount))
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
|
||||
return amount;
|
||||
}
|
||||
|
||||
UniValue ValueFromAmount(const CAmount& amount)
|
||||
{
|
||||
bool sign = amount < 0;
|
||||
int64_t n_abs = (sign ? -amount : amount);
|
||||
int64_t quotient = n_abs / COIN;
|
||||
int64_t remainder = n_abs % COIN;
|
||||
return UniValue(UniValue::VNUM,
|
||||
strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
|
||||
}
|
||||
|
||||
uint256 ParseHashV(const UniValue& v, string strName)
|
||||
{
|
||||
string strHex;
|
||||
if (v.isStr())
|
||||
strHex = v.get_str();
|
||||
if (!IsHex(strHex)) // Note: IsHex("") is false
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
uint256 result;
|
||||
result.SetHex(strHex);
|
||||
return result;
|
||||
}
|
||||
uint256 ParseHashO(const UniValue& o, string strKey)
|
||||
{
|
||||
return ParseHashV(find_value(o, strKey), strKey);
|
||||
}
|
||||
vector<unsigned char> ParseHexV(const UniValue& v, string strName)
|
||||
{
|
||||
string strHex;
|
||||
if (v.isStr())
|
||||
strHex = v.get_str();
|
||||
if (!IsHex(strHex))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
return ParseHex(strHex);
|
||||
}
|
||||
vector<unsigned char> ParseHexO(const UniValue& o, string strKey)
|
||||
{
|
||||
return ParseHexV(find_value(o, strKey), strKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This interface may still be subject to change.
|
||||
*/
|
||||
|
||||
std::string CRPCTable::help(const std::string& strCommand) const
|
||||
{
|
||||
string strRet;
|
||||
string category;
|
||||
set<rpcfn_type> setDone;
|
||||
vector<pair<string, const CRPCCommand*> > vCommands;
|
||||
|
||||
for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
|
||||
vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
|
||||
sort(vCommands.begin(), vCommands.end());
|
||||
|
||||
BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands)
|
||||
{
|
||||
const CRPCCommand *pcmd = command.second;
|
||||
string strMethod = pcmd->name;
|
||||
// We already filter duplicates, but these deprecated screw up the sort order
|
||||
if (strMethod.find("label") != string::npos)
|
||||
continue;
|
||||
if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
|
||||
continue;
|
||||
try
|
||||
{
|
||||
UniValue params;
|
||||
rpcfn_type pfn = pcmd->actor;
|
||||
if (setDone.insert(pfn).second)
|
||||
(*pfn)(params, true);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Help text is returned in an exception
|
||||
string strHelp = string(e.what());
|
||||
if (strCommand == "")
|
||||
{
|
||||
if (strHelp.find('\n') != string::npos)
|
||||
strHelp = strHelp.substr(0, strHelp.find('\n'));
|
||||
|
||||
if (category != pcmd->category)
|
||||
{
|
||||
if (!category.empty())
|
||||
strRet += "\n";
|
||||
category = pcmd->category;
|
||||
string firstLetter = category.substr(0,1);
|
||||
boost::to_upper(firstLetter);
|
||||
strRet += "== " + firstLetter + category.substr(1) + " ==\n";
|
||||
}
|
||||
}
|
||||
strRet += strHelp + "\n";
|
||||
}
|
||||
}
|
||||
if (strRet == "")
|
||||
strRet = strprintf("help: unknown command: %s\n", strCommand);
|
||||
strRet = strRet.substr(0,strRet.size()-1);
|
||||
return strRet;
|
||||
}
|
||||
|
||||
UniValue help(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"help ( \"command\" )\n"
|
||||
"\nList all commands, or get help for a specified command.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"command\" (string, optional) The command to get help on\n"
|
||||
"\nResult:\n"
|
||||
"\"text\" (string) The help text\n"
|
||||
);
|
||||
|
||||
string strCommand;
|
||||
if (params.size() > 0)
|
||||
strCommand = params[0].get_str();
|
||||
|
||||
return tableRPC.help(strCommand);
|
||||
}
|
||||
|
||||
|
||||
UniValue stop(const UniValue& params, bool fHelp)
|
||||
{
|
||||
// Accept the deprecated and ignored 'detach' boolean argument
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"stop\n"
|
||||
"\nStop Zcash server.");
|
||||
// Event loop will exit after current HTTP requests have been handled, so
|
||||
// this reply will get back to the client.
|
||||
StartShutdown();
|
||||
return "Zcash server stopping";
|
||||
}
|
||||
|
||||
/**
|
||||
* Call Table
|
||||
*/
|
||||
static const CRPCCommand vRPCCommands[] =
|
||||
{ // category name actor (function) okSafeMode
|
||||
// --------------------- ------------------------ ----------------------- ----------
|
||||
/* Overall control/query calls */
|
||||
{ "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */
|
||||
{ "control", "help", &help, true },
|
||||
{ "control", "stop", &stop, true },
|
||||
|
||||
/* P2P networking */
|
||||
{ "network", "getnetworkinfo", &getnetworkinfo, true },
|
||||
{ "network", "getdeprecationinfo", &getdeprecationinfo, true },
|
||||
{ "network", "addnode", &addnode, true },
|
||||
{ "network", "disconnectnode", &disconnectnode, true },
|
||||
{ "network", "getaddednodeinfo", &getaddednodeinfo, true },
|
||||
{ "network", "getconnectioncount", &getconnectioncount, true },
|
||||
{ "network", "getnettotals", &getnettotals, true },
|
||||
{ "network", "getpeerinfo", &getpeerinfo, true },
|
||||
{ "network", "ping", &ping, true },
|
||||
{ "network", "setban", &setban, true },
|
||||
{ "network", "listbanned", &listbanned, true },
|
||||
{ "network", "clearbanned", &clearbanned, true },
|
||||
|
||||
/* Block chain and UTXO */
|
||||
{ "blockchain", "getblockchaininfo", &getblockchaininfo, true },
|
||||
{ "blockchain", "getbestblockhash", &getbestblockhash, true },
|
||||
{ "blockchain", "getblockcount", &getblockcount, true },
|
||||
{ "blockchain", "getblock", &getblock, true },
|
||||
{ "blockchain", "getblockhash", &getblockhash, true },
|
||||
{ "blockchain", "getblockheader", &getblockheader, true },
|
||||
{ "blockchain", "getchaintips", &getchaintips, true },
|
||||
{ "blockchain", "getdifficulty", &getdifficulty, true },
|
||||
{ "blockchain", "getmempoolinfo", &getmempoolinfo, true },
|
||||
{ "blockchain", "getrawmempool", &getrawmempool, true },
|
||||
{ "blockchain", "gettxout", &gettxout, true },
|
||||
{ "blockchain", "gettxoutproof", &gettxoutproof, true },
|
||||
{ "blockchain", "verifytxoutproof", &verifytxoutproof, true },
|
||||
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
|
||||
{ "blockchain", "verifychain", &verifychain, true },
|
||||
|
||||
/* Mining */
|
||||
{ "mining", "getblocktemplate", &getblocktemplate, true },
|
||||
{ "mining", "getmininginfo", &getmininginfo, true },
|
||||
{ "mining", "getlocalsolps", &getlocalsolps, true },
|
||||
{ "mining", "getnetworksolps", &getnetworksolps, true },
|
||||
{ "mining", "getnetworkhashps", &getnetworkhashps, true },
|
||||
{ "mining", "prioritisetransaction", &prioritisetransaction, true },
|
||||
{ "mining", "submitblock", &submitblock, true },
|
||||
{ "mining", "getblocksubsidy", &getblocksubsidy, true },
|
||||
|
||||
#ifdef ENABLE_MINING
|
||||
/* Coin generation */
|
||||
{ "generating", "getgenerate", &getgenerate, true },
|
||||
{ "generating", "setgenerate", &setgenerate, true },
|
||||
{ "generating", "generate", &generate, true },
|
||||
#endif
|
||||
|
||||
/* Raw transactions */
|
||||
{ "rawtransactions", "createrawtransaction", &createrawtransaction, true },
|
||||
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, true },
|
||||
{ "rawtransactions", "decodescript", &decodescript, true },
|
||||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, true },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false },
|
||||
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */
|
||||
|
||||
/* Utility functions */
|
||||
{ "util", "createmultisig", &createmultisig, true },
|
||||
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
|
||||
{ "util", "verifymessage", &verifymessage, true },
|
||||
{ "util", "estimatefee", &estimatefee, true },
|
||||
{ "util", "estimatepriority", &estimatepriority, true },
|
||||
{ "util", "z_validateaddress", &z_validateaddress, true }, /* uses wallet if enabled */
|
||||
|
||||
/* Not shown in help */
|
||||
{ "hidden", "invalidateblock", &invalidateblock, true },
|
||||
{ "hidden", "reconsiderblock", &reconsiderblock, true },
|
||||
{ "hidden", "setmocktime", &setmocktime, true },
|
||||
};
|
||||
|
||||
CRPCTable::CRPCTable()
|
||||
{
|
||||
unsigned int vcidx;
|
||||
for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
|
||||
{
|
||||
const CRPCCommand *pcmd;
|
||||
|
||||
pcmd = &vRPCCommands[vcidx];
|
||||
mapCommands[pcmd->name] = pcmd;
|
||||
}
|
||||
}
|
||||
|
||||
const CRPCCommand *CRPCTable::operator[](const std::string &name) const
|
||||
{
|
||||
map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
|
||||
if (it == mapCommands.end())
|
||||
return NULL;
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
|
||||
{
|
||||
if (IsRPCRunning())
|
||||
return false;
|
||||
|
||||
// don't allow overwriting for now
|
||||
map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
|
||||
if (it != mapCommands.end())
|
||||
return false;
|
||||
|
||||
mapCommands[name] = pcmd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StartRPC()
|
||||
{
|
||||
LogPrint("rpc", "Starting RPC\n");
|
||||
fRPCRunning = true;
|
||||
g_rpcSignals.Started();
|
||||
|
||||
// Launch one async rpc worker. The ability to launch multiple workers is not recommended at present and thus the option is disabled.
|
||||
getAsyncRPCQueue()->addWorker();
|
||||
/*
|
||||
int n = GetArg("-rpcasyncthreads", 1);
|
||||
if (n<1) {
|
||||
LogPrintf("ERROR: Invalid value %d for -rpcasyncthreads. Must be at least 1.\n", n);
|
||||
strerr = strprintf(_("An error occurred while setting up the Async RPC threads, invalid parameter value of %d (must be at least 1)."), n);
|
||||
uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR);
|
||||
StartShutdown();
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < n; i++)
|
||||
getAsyncRPCQueue()->addWorker();
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void InterruptRPC()
|
||||
{
|
||||
LogPrint("rpc", "Interrupting RPC\n");
|
||||
// Interrupt e.g. running longpolls
|
||||
fRPCRunning = false;
|
||||
}
|
||||
|
||||
void StopRPC()
|
||||
{
|
||||
LogPrint("rpc", "Stopping RPC\n");
|
||||
deadlineTimers.clear();
|
||||
g_rpcSignals.Stopped();
|
||||
|
||||
// Tells async queue to cancel all operations and shutdown.
|
||||
LogPrintf("%s: waiting for async rpc workers to stop\n", __func__);
|
||||
getAsyncRPCQueue()->closeAndWait();
|
||||
}
|
||||
|
||||
bool IsRPCRunning()
|
||||
{
|
||||
return fRPCRunning;
|
||||
}
|
||||
|
||||
void SetRPCWarmupStatus(const std::string& newStatus)
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
rpcWarmupStatus = newStatus;
|
||||
}
|
||||
|
||||
void SetRPCWarmupFinished()
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
assert(fRPCInWarmup);
|
||||
fRPCInWarmup = false;
|
||||
}
|
||||
|
||||
bool RPCIsInWarmup(std::string *outStatus)
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
if (outStatus)
|
||||
*outStatus = rpcWarmupStatus;
|
||||
return fRPCInWarmup;
|
||||
}
|
||||
|
||||
void JSONRequest::parse(const UniValue& valRequest)
|
||||
{
|
||||
// Parse request
|
||||
if (!valRequest.isObject())
|
||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
|
||||
const UniValue& request = valRequest.get_obj();
|
||||
|
||||
// Parse id now so errors from here on will have the id
|
||||
id = find_value(request, "id");
|
||||
|
||||
// Parse method
|
||||
UniValue valMethod = find_value(request, "method");
|
||||
if (valMethod.isNull())
|
||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
|
||||
if (!valMethod.isStr())
|
||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
|
||||
strMethod = valMethod.get_str();
|
||||
if (strMethod != "getblocktemplate")
|
||||
LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
|
||||
|
||||
// Parse params
|
||||
UniValue valParams = find_value(request, "params");
|
||||
if (valParams.isArray())
|
||||
params = valParams.get_array();
|
||||
else if (valParams.isNull())
|
||||
params = UniValue(UniValue::VARR);
|
||||
else
|
||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
|
||||
}
|
||||
|
||||
static UniValue JSONRPCExecOne(const UniValue& req)
|
||||
{
|
||||
UniValue rpc_result(UniValue::VOBJ);
|
||||
|
||||
JSONRequest jreq;
|
||||
try {
|
||||
jreq.parse(req);
|
||||
|
||||
UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
|
||||
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
|
||||
}
|
||||
catch (const UniValue& objError)
|
||||
{
|
||||
rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
rpc_result = JSONRPCReplyObj(NullUniValue,
|
||||
JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
||||
}
|
||||
|
||||
return rpc_result;
|
||||
}
|
||||
|
||||
std::string JSONRPCExecBatch(const UniValue& vReq)
|
||||
{
|
||||
UniValue ret(UniValue::VARR);
|
||||
for (size_t reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
|
||||
ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
|
||||
|
||||
return ret.write() + "\n";
|
||||
}
|
||||
|
||||
UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const
|
||||
{
|
||||
// Return immediately if in warmup
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
if (fRPCInWarmup)
|
||||
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
|
||||
}
|
||||
|
||||
// Find method
|
||||
const CRPCCommand *pcmd = tableRPC[strMethod];
|
||||
if (!pcmd)
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
|
||||
|
||||
g_rpcSignals.PreCommand(*pcmd);
|
||||
|
||||
try
|
||||
{
|
||||
// Execute
|
||||
return pcmd->actor(params, false);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw JSONRPCError(RPC_MISC_ERROR, e.what());
|
||||
}
|
||||
|
||||
g_rpcSignals.PostCommand(*pcmd);
|
||||
}
|
||||
|
||||
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
|
||||
{
|
||||
return "> zcash-cli " + methodname + " " + args + "\n";
|
||||
}
|
||||
|
||||
std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
|
||||
{
|
||||
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
|
||||
"\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8232/\n";
|
||||
}
|
||||
|
||||
string experimentalDisabledHelpMsg(const string& rpc, const string& enableArg)
|
||||
{
|
||||
return "\nWARNING: " + rpc + " is disabled.\n"
|
||||
"To enable it, restart zcashd with the -experimentalfeatures and\n"
|
||||
"-" + enableArg + " commandline options, or add these two lines\n"
|
||||
"to the zcash.conf file:\n\n"
|
||||
"experimentalfeatures=1\n"
|
||||
+ enableArg + "=1\n";
|
||||
}
|
||||
|
||||
void RPCRegisterTimerInterface(RPCTimerInterface *iface)
|
||||
{
|
||||
timerInterfaces.push_back(iface);
|
||||
}
|
||||
|
||||
void RPCUnregisterTimerInterface(RPCTimerInterface *iface)
|
||||
{
|
||||
std::vector<RPCTimerInterface*>::iterator i = std::find(timerInterfaces.begin(), timerInterfaces.end(), iface);
|
||||
assert(i != timerInterfaces.end());
|
||||
timerInterfaces.erase(i);
|
||||
}
|
||||
|
||||
void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds)
|
||||
{
|
||||
if (timerInterfaces.empty())
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
|
||||
deadlineTimers.erase(name);
|
||||
RPCTimerInterface* timerInterface = timerInterfaces[0];
|
||||
LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
|
||||
deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000))));
|
||||
}
|
||||
|
||||
CRPCTable tableRPC;
|
||||
|
||||
// Return async rpc queue
|
||||
std::shared_ptr<AsyncRPCQueue> getAsyncRPCQueue()
|
||||
{
|
||||
return AsyncRPCQueue::sharedInstance();
|
||||
}
|
||||
267
src/rpc/server.h
Normal file
267
src/rpc/server.h
Normal file
@@ -0,0 +1,267 @@
|
||||
// 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 http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_RPCSERVER_H
|
||||
#define BITCOIN_RPCSERVER_H
|
||||
|
||||
#include "amount.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
class AsyncRPCQueue;
|
||||
class CRPCCommand;
|
||||
|
||||
namespace RPCServer
|
||||
{
|
||||
void OnStarted(boost::function<void ()> slot);
|
||||
void OnStopped(boost::function<void ()> slot);
|
||||
void OnPreCommand(boost::function<void (const CRPCCommand&)> slot);
|
||||
void OnPostCommand(boost::function<void (const CRPCCommand&)> slot);
|
||||
}
|
||||
|
||||
class CBlockIndex;
|
||||
class CNetAddr;
|
||||
|
||||
class JSONRequest
|
||||
{
|
||||
public:
|
||||
UniValue id;
|
||||
std::string strMethod;
|
||||
UniValue params;
|
||||
|
||||
JSONRequest() { id = NullUniValue; }
|
||||
void parse(const UniValue& valRequest);
|
||||
};
|
||||
|
||||
/** Query whether RPC is running */
|
||||
bool IsRPCRunning();
|
||||
|
||||
/** Get the async queue*/
|
||||
std::shared_ptr<AsyncRPCQueue> getAsyncRPCQueue();
|
||||
|
||||
|
||||
/**
|
||||
* Set the RPC warmup status. When this is done, all RPC calls will error out
|
||||
* immediately with RPC_IN_WARMUP.
|
||||
*/
|
||||
void SetRPCWarmupStatus(const std::string& newStatus);
|
||||
/* Mark warmup as done. RPC calls will be processed from now on. */
|
||||
void SetRPCWarmupFinished();
|
||||
|
||||
/* returns the current warmup state. */
|
||||
bool RPCIsInWarmup(std::string *statusOut);
|
||||
|
||||
/**
|
||||
* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
|
||||
* the right number of arguments are passed, just that any passed are the correct type.
|
||||
* Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type));
|
||||
*/
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const std::list<UniValue::VType>& typesExpected, bool fAllowNull=false);
|
||||
|
||||
/*
|
||||
Check for expected keys/value types in an Object.
|
||||
Use like: RPCTypeCheckObj(object, boost::assign::map_list_of("name", str_type)("value", int_type));
|
||||
*/
|
||||
void RPCTypeCheckObj(const UniValue& o,
|
||||
const std::map<std::string, UniValue::VType>& typesExpected, bool fAllowNull=false);
|
||||
|
||||
/** Opaque base class for timers returned by NewTimerFunc.
|
||||
* This provides no methods at the moment, but makes sure that delete
|
||||
* cleans up the whole state.
|
||||
*/
|
||||
class RPCTimerBase
|
||||
{
|
||||
public:
|
||||
virtual ~RPCTimerBase() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* RPC timer "driver".
|
||||
*/
|
||||
class RPCTimerInterface
|
||||
{
|
||||
public:
|
||||
virtual ~RPCTimerInterface() {}
|
||||
/** Implementation name */
|
||||
virtual const char *Name() = 0;
|
||||
/** Factory function for timers.
|
||||
* RPC will call the function to create a timer that will call func in *millis* milliseconds.
|
||||
* @note As the RPC mechanism is backend-neutral, it can use different implementations of timers.
|
||||
* This is needed to cope with the case in which there is no HTTP server, but
|
||||
* only GUI RPC console, and to break the dependency of rpcserver on httprpc.
|
||||
*/
|
||||
virtual RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis) = 0;
|
||||
};
|
||||
|
||||
/** Register factory function for timers */
|
||||
void RPCRegisterTimerInterface(RPCTimerInterface *iface);
|
||||
/** Unregister factory function for timers */
|
||||
void RPCUnregisterTimerInterface(RPCTimerInterface *iface);
|
||||
|
||||
/**
|
||||
* Run func nSeconds from now.
|
||||
* Overrides previous timer <name> (if any).
|
||||
*/
|
||||
void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds);
|
||||
|
||||
typedef UniValue(*rpcfn_type)(const UniValue& params, bool fHelp);
|
||||
|
||||
class CRPCCommand
|
||||
{
|
||||
public:
|
||||
std::string category;
|
||||
std::string name;
|
||||
rpcfn_type actor;
|
||||
bool okSafeMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bitcoin RPC command dispatcher.
|
||||
*/
|
||||
class CRPCTable
|
||||
{
|
||||
private:
|
||||
std::map<std::string, const CRPCCommand*> mapCommands;
|
||||
public:
|
||||
CRPCTable();
|
||||
const CRPCCommand* operator[](const std::string& name) const;
|
||||
std::string help(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* Execute a method.
|
||||
* @param method Method to execute
|
||||
* @param params UniValue Array of arguments (JSON objects)
|
||||
* @returns Result of the call.
|
||||
* @throws an exception (UniValue) when an error happens.
|
||||
*/
|
||||
UniValue execute(const std::string &method, const UniValue ¶ms) const;
|
||||
|
||||
|
||||
/**
|
||||
* Appends a CRPCCommand to the dispatch table.
|
||||
* Returns false if RPC server is already running (dump concurrency protection).
|
||||
* Commands cannot be overwritten (returns false).
|
||||
*/
|
||||
bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
|
||||
};
|
||||
|
||||
extern CRPCTable tableRPC;
|
||||
|
||||
/**
|
||||
* Utilities: convert hex-encoded Values
|
||||
* (throws error if not hex).
|
||||
*/
|
||||
extern uint256 ParseHashV(const UniValue& v, std::string strName);
|
||||
extern uint256 ParseHashO(const UniValue& o, std::string strKey);
|
||||
extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
|
||||
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
|
||||
|
||||
extern int64_t nWalletUnlockTime;
|
||||
extern CAmount AmountFromValue(const UniValue& value);
|
||||
extern UniValue ValueFromAmount(const CAmount& amount);
|
||||
extern double GetDifficulty(const CBlockIndex* blockindex = NULL);
|
||||
extern double GetNetworkDifficulty(const CBlockIndex* blockindex = NULL);
|
||||
extern std::string HelpRequiringPassphrase();
|
||||
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
|
||||
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
|
||||
|
||||
extern void EnsureWalletIsUnlocked();
|
||||
|
||||
extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp
|
||||
extern UniValue getpeerinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue ping(const UniValue& params, bool fHelp);
|
||||
extern UniValue addnode(const UniValue& params, bool fHelp);
|
||||
extern UniValue disconnectnode(const UniValue& params, bool fHelp);
|
||||
extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue getnettotals(const UniValue& params, bool fHelp);
|
||||
extern UniValue setban(const UniValue& params, bool fHelp);
|
||||
extern UniValue listbanned(const UniValue& params, bool fHelp);
|
||||
extern UniValue clearbanned(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue getgenerate(const UniValue& params, bool fHelp); // in rpcmining.cpp
|
||||
extern UniValue setgenerate(const UniValue& params, bool fHelp);
|
||||
extern UniValue generate(const UniValue& params, bool fHelp);
|
||||
extern UniValue getlocalsolps(const UniValue& params, bool fHelp);
|
||||
extern UniValue getnetworksolps(const UniValue& params, bool fHelp);
|
||||
extern UniValue getnetworkhashps(const UniValue& params, bool fHelp);
|
||||
extern UniValue getmininginfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue prioritisetransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblocktemplate(const UniValue& params, bool fHelp);
|
||||
extern UniValue submitblock(const UniValue& params, bool fHelp);
|
||||
extern UniValue estimatefee(const UniValue& params, bool fHelp);
|
||||
extern UniValue estimatepriority(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue verifymessage(const UniValue& params, bool fHelp);
|
||||
extern UniValue createmultisig(const UniValue& params, bool fHelp);
|
||||
extern UniValue validateaddress(const UniValue& params, bool fHelp);
|
||||
extern UniValue getinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblockchaininfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue getnetworkinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue getdeprecationinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue setmocktime(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue getrawtransaction(const UniValue& params, bool fHelp); // in rcprawtransaction.cpp
|
||||
extern UniValue listunspent(const UniValue& params, bool fHelp);
|
||||
extern UniValue lockunspent(const UniValue& params, bool fHelp);
|
||||
extern UniValue listlockunspent(const UniValue& params, bool fHelp);
|
||||
extern UniValue createrawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue decoderawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue decodescript(const UniValue& params, bool fHelp);
|
||||
extern UniValue signrawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue sendrawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue gettxoutproof(const UniValue& params, bool fHelp);
|
||||
extern UniValue verifytxoutproof(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue getblockcount(const UniValue& params, bool fHelp); // in rpcblockchain.cpp
|
||||
extern UniValue getbestblockhash(const UniValue& params, bool fHelp);
|
||||
extern UniValue getdifficulty(const UniValue& params, bool fHelp);
|
||||
extern UniValue settxfee(const UniValue& params, bool fHelp);
|
||||
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue getrawmempool(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblockhash(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblockheader(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblock(const UniValue& params, bool fHelp);
|
||||
extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue gettxout(const UniValue& params, bool fHelp);
|
||||
extern UniValue verifychain(const UniValue& params, bool fHelp);
|
||||
extern UniValue getchaintips(const UniValue& params, bool fHelp);
|
||||
extern UniValue invalidateblock(const UniValue& params, bool fHelp);
|
||||
extern UniValue reconsiderblock(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue getblocksubsidy(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue z_getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_listaddresses(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_listunspent(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_getbalance(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_gettotalbalance(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_mergetoaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_sendmany(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_getoperationresult(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_listoperationids(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_validateaddress(const UniValue& params, bool fHelp); // in rpcmisc.cpp
|
||||
|
||||
bool StartRPC();
|
||||
void InterruptRPC();
|
||||
void StopRPC();
|
||||
std::string JSONRPCExecBatch(const UniValue& vReq);
|
||||
|
||||
extern std::string experimentalDisabledHelpMsg(const std::string& rpc, const std::string& enableArg);
|
||||
|
||||
#endif // BITCOIN_RPCSERVER_H
|
||||
Reference in New Issue
Block a user