aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp691
-rw-r--r--src/rpc/blockchain.h3
-rw-r--r--src/rpc/client.cpp22
-rw-r--r--src/rpc/mining.cpp43
-rw-r--r--src/rpc/misc.cpp75
-rw-r--r--src/rpc/net.cpp26
-rw-r--r--src/rpc/protocol.h4
-rw-r--r--src/rpc/rawtransaction.cpp771
-rw-r--r--src/rpc/rawtransaction.h3
-rw-r--r--src/rpc/safemode.cpp14
-rw-r--r--src/rpc/safemode.h12
-rw-r--r--src/rpc/server.cpp12
12 files changed, 1400 insertions, 276 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 03c097197f..012e3e3ac1 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -6,6 +6,7 @@
#include <rpc/blockchain.h>
#include <amount.h>
+#include <base58.h>
#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
@@ -13,6 +14,8 @@
#include <consensus/validation.h>
#include <validation.h>
#include <core_io.h>
+#include <index/txindex.h>
+#include <key_io.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
@@ -27,10 +30,12 @@
#include <validationinterface.h>
#include <warnings.h>
+#include <assert.h>
#include <stdint.h>
#include <univalue.h>
+#include <boost/algorithm/string.hpp>
#include <boost/thread/thread.hpp> // boost::thread::interrupt
#include <memory>
@@ -47,17 +52,13 @@ static std::mutex cs_blockchange;
static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
-/* Calculate the difficulty for a given block index,
- * or the block index of the given chain.
+/* Calculate the difficulty for a given block index.
*/
-double GetDifficulty(const CChain& chain, const CBlockIndex* blockindex)
+double GetDifficulty(const CBlockIndex* blockindex)
{
if (blockindex == nullptr)
{
- if (chain.Tip() == nullptr)
- return 1.0;
- else
- blockindex = chain.Tip();
+ return 1.0;
}
int nShift = (blockindex->nBits >> 24) & 0xff;
@@ -78,11 +79,6 @@ double GetDifficulty(const CChain& chain, const CBlockIndex* blockindex)
return dDiff;
}
-double GetDifficulty(const CBlockIndex* blockindex)
-{
- return GetDifficulty(chainActive, blockindex);
-}
-
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
AssertLockHeld(cs_main);
@@ -103,6 +99,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
result.pushKV("bits", strprintf("%08x", blockindex->nBits));
result.pushKV("difficulty", GetDifficulty(blockindex));
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
+ result.pushKV("nTx", (uint64_t)blockindex->nTx);
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
@@ -148,6 +145,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.pushKV("bits", strprintf("%08x", block.nBits));
result.pushKV("difficulty", GetDifficulty(blockindex));
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
+ result.pushKV("nTx", (uint64_t)blockindex->nTx);
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
@@ -157,7 +155,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
return result;
}
-UniValue getblockcount(const JSONRPCRequest& request)
+static UniValue getblockcount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -174,7 +172,7 @@ UniValue getblockcount(const JSONRPCRequest& request)
return chainActive.Height();
}
-UniValue getbestblockhash(const JSONRPCRequest& request)
+static UniValue getbestblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -201,7 +199,7 @@ void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
cond_blockchange.notify_all();
}
-UniValue waitfornewblock(const JSONRPCRequest& request)
+static UniValue waitfornewblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -239,7 +237,7 @@ UniValue waitfornewblock(const JSONRPCRequest& request)
return ret;
}
-UniValue waitforblock(const JSONRPCRequest& request)
+static UniValue waitforblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -281,7 +279,7 @@ UniValue waitforblock(const JSONRPCRequest& request)
return ret;
}
-UniValue waitforblockheight(const JSONRPCRequest& request)
+static UniValue waitforblockheight(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -323,7 +321,7 @@ UniValue waitforblockheight(const JSONRPCRequest& request)
return ret;
}
-UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
+static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
@@ -338,7 +336,7 @@ UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getdifficulty(const JSONRPCRequest& request)
+static UniValue getdifficulty(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -352,10 +350,10 @@ UniValue getdifficulty(const JSONRPCRequest& request)
);
LOCK(cs_main);
- return GetDifficulty();
+ return GetDifficulty(chainActive.Tip());
}
-std::string EntryDescriptionString()
+static std::string EntryDescriptionString()
{
return " \"size\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
" \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n"
@@ -373,7 +371,7 @@ std::string EntryDescriptionString()
" \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
" \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n"
" \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n"
- " \"descendent\" : n, (numeric) number of in-mempool ancestor transactions (including this one) in " + CURRENCY_UNIT + "\n"
+ " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n"
" }\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
@@ -383,7 +381,7 @@ std::string EntryDescriptionString()
" ... ]\n";
}
-void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
+static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCKS_REQUIRED(::mempool.cs)
{
AssertLockHeld(mempool.cs);
@@ -460,7 +458,7 @@ UniValue mempoolToJSON(bool fVerbose)
}
}
-UniValue getrawmempool(const JSONRPCRequest& request)
+static UniValue getrawmempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -492,7 +490,7 @@ UniValue getrawmempool(const JSONRPCRequest& request)
return mempoolToJSON(fVerbose);
}
-UniValue getmempoolancestors(const JSONRPCRequest& request)
+static UniValue getmempoolancestors(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
@@ -556,7 +554,7 @@ UniValue getmempoolancestors(const JSONRPCRequest& request)
}
}
-UniValue getmempooldescendants(const JSONRPCRequest& request)
+static UniValue getmempooldescendants(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
@@ -620,7 +618,7 @@ UniValue getmempooldescendants(const JSONRPCRequest& request)
}
}
-UniValue getmempoolentry(const JSONRPCRequest& request)
+static UniValue getmempoolentry(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
@@ -653,7 +651,7 @@ UniValue getmempoolentry(const JSONRPCRequest& request)
return info;
}
-UniValue getblockhash(const JSONRPCRequest& request)
+static UniValue getblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -678,7 +676,7 @@ UniValue getblockhash(const JSONRPCRequest& request)
return pblockindex->GetBlockHash().GetHex();
}
-UniValue getblockheader(const JSONRPCRequest& request)
+static UniValue getblockheader(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -702,6 +700,7 @@ UniValue getblockheader(const JSONRPCRequest& request)
" \"bits\" : \"1d00ffff\", (string) The bits\n"
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
" \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n"
+ " \"nTx\" : n, (numeric) The number of transactions in the block.\n"
" \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
" \"nextblockhash\" : \"hash\", (string) The hash of the next block\n"
"}\n"
@@ -737,7 +736,26 @@ UniValue getblockheader(const JSONRPCRequest& request)
return blockheaderToJSON(pblockindex);
}
-UniValue getblock(const JSONRPCRequest& request)
+static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
+{
+ CBlock block;
+ if (IsBlockPruned(pblockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
+ }
+
+ if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
+ // Block not found on disk. This could be because we have the block
+ // header in our index but don't have the block (for example if a
+ // non-whitelisted node sends us an unrequested long chain of valid
+ // blocks, we add the headers to our index, but don't accept the
+ // block).
+ throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
+ }
+
+ return block;
+}
+
+static UniValue getblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -771,6 +789,7 @@ UniValue getblock(const JSONRPCRequest& request)
" \"bits\" : \"1d00ffff\", (string) The bits\n"
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
" \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n"
+ " \"nTx\" : n, (numeric) The number of transactions in the block.\n"
" \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
" \"nextblockhash\" : \"hash\" (string) The hash of the next block\n"
"}\n"
@@ -805,17 +824,7 @@ UniValue getblock(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- CBlock block;
- if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0)
- throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
-
- if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
- // Block not found on disk. This could be because we have the block
- // header in our index but don't have the block (for example if a
- // non-whitelisted node sends us an unrequested long chain of valid
- // blocks, we add the headers to our index, but don't accept the
- // block).
- throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
+ const CBlock block = GetBlockChecked(pblockindex);
if (verbosity <= 0)
{
@@ -848,7 +857,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
ss << hash;
ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u);
stats.nTransactions++;
- for (const auto output : outputs) {
+ for (const auto& output : outputs) {
ss << VARINT(output.first + 1);
ss << output.second.out.scriptPubKey;
ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
@@ -899,7 +908,7 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
return true;
}
-UniValue pruneblockchain(const JSONRPCRequest& request)
+static UniValue pruneblockchain(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -940,7 +949,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
else if (height > chainHeight)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
- LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.");
+ LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n");
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
@@ -948,7 +957,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
return uint64_t(height);
}
-UniValue gettxoutsetinfo(const JSONRPCRequest& request)
+static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -958,9 +967,9 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"height\":n, (numeric) The current block height (index)\n"
- " \"bestblock\": \"hex\", (string) the best block hash hex\n"
- " \"transactions\": n, (numeric) The number of transactions\n"
- " \"txouts\": n, (numeric) The number of output transactions\n"
+ " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n"
+ " \"transactions\": n, (numeric) The number of transactions with unspent outputs\n"
+ " \"txouts\": n, (numeric) The number of unspent transaction outputs\n"
" \"bogosize\": n, (numeric) A meaningless metric for UTXO set size\n"
" \"hash_serialized_2\": \"hash\", (string) The serialized hash\n"
" \"disk_size\": n, (numeric) The estimated size of the chainstate on disk\n"
@@ -1003,7 +1012,7 @@ UniValue gettxout(const JSONRPCRequest& request)
" Note that an unspent output that is spent in the mempool won't appear.\n"
"\nResult:\n"
"{\n"
- " \"bestblock\" : \"hash\", (string) the block hash\n"
+ " \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n"
" \"scriptPubKey\" : { (json object)\n"
@@ -1069,7 +1078,7 @@ UniValue gettxout(const JSONRPCRequest& request)
return ret;
}
-UniValue verifychain(const JSONRPCRequest& request)
+static UniValue verifychain(const JSONRPCRequest& request)
{
int nCheckLevel = gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL);
int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
@@ -1159,7 +1168,7 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
return rv;
}
-void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
{
// Deployments with timeout value of 0 are hidden.
// A timeout value of 0 guarantees a softfork will never be activated.
@@ -1229,7 +1238,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.pushKV("blocks", (int)chainActive.Height());
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex());
- obj.pushKV("difficulty", (double)GetDifficulty());
+ obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
obj.pushKV("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()));
obj.pushKV("initialblockdownload", IsInitialBlockDownload());
@@ -1285,7 +1294,7 @@ struct CompareBlocksByHeight
}
};
-UniValue getchaintips(const JSONRPCRequest& request)
+static UniValue getchaintips(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -1402,7 +1411,7 @@ UniValue mempoolInfoToJSON()
return ret;
}
-UniValue getmempoolinfo(const JSONRPCRequest& request)
+static UniValue getmempoolinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -1425,7 +1434,7 @@ UniValue getmempoolinfo(const JSONRPCRequest& request)
return mempoolInfoToJSON();
}
-UniValue preciousblock(const JSONRPCRequest& request)
+static UniValue preciousblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -1463,7 +1472,7 @@ UniValue preciousblock(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue invalidateblock(const JSONRPCRequest& request)
+static UniValue invalidateblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -1502,7 +1511,7 @@ UniValue invalidateblock(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue reconsiderblock(const JSONRPCRequest& request)
+static UniValue reconsiderblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -1540,7 +1549,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getchaintxstats(const JSONRPCRequest& request)
+static UniValue getchaintxstats(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
@@ -1614,7 +1623,285 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
return ret;
}
-UniValue savemempool(const JSONRPCRequest& request)
+template<typename T>
+static T CalculateTruncatedMedian(std::vector<T>& scores)
+{
+ size_t size = scores.size();
+ if (size == 0) {
+ return 0;
+ }
+
+ std::sort(scores.begin(), scores.end());
+ if (size % 2 == 0) {
+ return (scores[size / 2 - 1] + scores[size / 2]) / 2;
+ } else {
+ return scores[size / 2];
+ }
+}
+
+template<typename T>
+static inline bool SetHasKeys(const std::set<T>& set) {return false;}
+template<typename T, typename Tk, typename... Args>
+static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&... args)
+{
+ return (set.count(key) != 0) || SetHasKeys(set, args...);
+}
+
+// outpoint (needed for the utxo index) + nHeight + fCoinBase
+static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t) + sizeof(bool);
+
+static UniValue getblockstats(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) {
+ throw std::runtime_error(
+ "getblockstats hash_or_height ( stats )\n"
+ "\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
+ "It won't work for some heights with pruning.\n"
+ "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n"
+ "\nArguments:\n"
+ "1. \"hash_or_height\" (string or numeric, required) The block hash or height of the target block\n"
+ "2. \"stats\" (array, optional) Values to plot, by default all values (see result below)\n"
+ " [\n"
+ " \"height\", (string, optional) Selected statistic\n"
+ " \"time\", (string, optional) Selected statistic\n"
+ " ,...\n"
+ " ]\n"
+ "\nResult:\n"
+ "{ (json object)\n"
+ " \"avgfee\": xxxxx, (numeric) Average fee in the block\n"
+ " \"avgfeerate\": xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n"
+ " \"avgtxsize\": xxxxx, (numeric) Average transaction size\n"
+ " \"blockhash\": xxxxx, (string) The block hash (to check for potential reorgs)\n"
+ " \"height\": xxxxx, (numeric) The height of the block\n"
+ " \"ins\": xxxxx, (numeric) The number of inputs (excluding coinbase)\n"
+ " \"maxfee\": xxxxx, (numeric) Maximum fee in the block\n"
+ " \"maxfeerate\": xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n"
+ " \"maxtxsize\": xxxxx, (numeric) Maximum transaction size\n"
+ " \"medianfee\": xxxxx, (numeric) Truncated median fee in the block\n"
+ " \"medianfeerate\": xxxxx, (numeric) Truncated median feerate (in satoshis per virtual byte)\n"
+ " \"mediantime\": xxxxx, (numeric) The block median time past\n"
+ " \"mediantxsize\": xxxxx, (numeric) Truncated median transaction size\n"
+ " \"minfee\": xxxxx, (numeric) Minimum fee in the block\n"
+ " \"minfeerate\": xxxxx, (numeric) Minimum feerate (in satoshis per virtual byte)\n"
+ " \"mintxsize\": xxxxx, (numeric) Minimum transaction size\n"
+ " \"outs\": xxxxx, (numeric) The number of outputs\n"
+ " \"subsidy\": xxxxx, (numeric) The block subsidy\n"
+ " \"swtotal_size\": xxxxx, (numeric) Total size of all segwit transactions\n"
+ " \"swtotal_weight\": xxxxx, (numeric) Total weight of all segwit transactions divided by segwit scale factor (4)\n"
+ " \"swtxs\": xxxxx, (numeric) The number of segwit transactions\n"
+ " \"time\": xxxxx, (numeric) The block time\n"
+ " \"total_out\": xxxxx, (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])\n"
+ " \"total_size\": xxxxx, (numeric) Total size of all non-coinbase transactions\n"
+ " \"total_weight\": xxxxx, (numeric) Total weight of all non-coinbase transactions divided by segwit scale factor (4)\n"
+ " \"totalfee\": xxxxx, (numeric) The fee total\n"
+ " \"txs\": xxxxx, (numeric) The number of transactions (excluding coinbase)\n"
+ " \"utxo_increase\": xxxxx, (numeric) The increase/decrease in the number of unspent outputs\n"
+ " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
+ + HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
+ );
+ }
+
+ LOCK(cs_main);
+
+ CBlockIndex* pindex;
+ if (request.params[0].isNum()) {
+ const int height = request.params[0].get_int();
+ const int current_tip = chainActive.Height();
+ if (height < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
+ }
+ if (height > current_tip) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
+ }
+
+ pindex = chainActive[height];
+ } else {
+ const std::string strHash = request.params[0].get_str();
+ const uint256 hash(uint256S(strHash));
+ pindex = LookupBlockIndex(hash);
+ if (!pindex) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ if (!chainActive.Contains(pindex)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Block is not in chain %s", Params().NetworkIDString()));
+ }
+ }
+
+ assert(pindex != nullptr);
+
+ std::set<std::string> stats;
+ if (!request.params[1].isNull()) {
+ const UniValue stats_univalue = request.params[1].get_array();
+ for (unsigned int i = 0; i < stats_univalue.size(); i++) {
+ const std::string stat = stats_univalue[i].get_str();
+ stats.insert(stat);
+ }
+ }
+
+ const CBlock block = GetBlockChecked(pindex);
+
+ const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
+ const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
+ const bool do_medianfee = do_all || stats.count("medianfee") != 0;
+ const bool do_medianfeerate = do_all || stats.count("medianfeerate") != 0;
+ const bool loop_inputs = do_all || do_medianfee || do_medianfeerate ||
+ SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
+ const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
+ const bool do_calculate_size = do_mediantxsize ||
+ SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size");
+ const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "medianfeerate", "minfeerate", "maxfeerate");
+ const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
+
+ CAmount maxfee = 0;
+ CAmount maxfeerate = 0;
+ CAmount minfee = MAX_MONEY;
+ CAmount minfeerate = MAX_MONEY;
+ CAmount total_out = 0;
+ CAmount totalfee = 0;
+ int64_t inputs = 0;
+ int64_t maxtxsize = 0;
+ int64_t mintxsize = MAX_BLOCK_SERIALIZED_SIZE;
+ int64_t outputs = 0;
+ int64_t swtotal_size = 0;
+ int64_t swtotal_weight = 0;
+ int64_t swtxs = 0;
+ int64_t total_size = 0;
+ int64_t total_weight = 0;
+ int64_t utxo_size_inc = 0;
+ std::vector<CAmount> fee_array;
+ std::vector<CAmount> feerate_array;
+ std::vector<int64_t> txsize_array;
+
+ for (const auto& tx : block.vtx) {
+ outputs += tx->vout.size();
+
+ CAmount tx_total_out = 0;
+ if (loop_outputs) {
+ for (const CTxOut& out : tx->vout) {
+ tx_total_out += out.nValue;
+ utxo_size_inc += GetSerializeSize(out, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
+ }
+ }
+
+ if (tx->IsCoinBase()) {
+ continue;
+ }
+
+ inputs += tx->vin.size(); // Don't count coinbase's fake input
+ total_out += tx_total_out; // Don't count coinbase reward
+
+ int64_t tx_size = 0;
+ if (do_calculate_size) {
+
+ tx_size = tx->GetTotalSize();
+ if (do_mediantxsize) {
+ txsize_array.push_back(tx_size);
+ }
+ maxtxsize = std::max(maxtxsize, tx_size);
+ mintxsize = std::min(mintxsize, tx_size);
+ total_size += tx_size;
+ }
+
+ int64_t weight = 0;
+ if (do_calculate_weight) {
+ weight = GetTransactionWeight(*tx);
+ total_weight += weight;
+ }
+
+ if (do_calculate_sw && tx->HasWitness()) {
+ ++swtxs;
+ swtotal_size += tx_size;
+ swtotal_weight += weight;
+ }
+
+ if (loop_inputs) {
+
+ if (!g_txindex) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
+ }
+ CAmount tx_total_in = 0;
+ for (const CTxIn& in : tx->vin) {
+ CTransactionRef tx_in;
+ uint256 hashBlock;
+ if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock, false)) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)"));
+ }
+
+ CTxOut prevoutput = tx_in->vout[in.prevout.n];
+
+ tx_total_in += prevoutput.nValue;
+ utxo_size_inc -= GetSerializeSize(prevoutput, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
+ }
+
+ CAmount txfee = tx_total_in - tx_total_out;
+ assert(MoneyRange(txfee));
+ if (do_medianfee) {
+ fee_array.push_back(txfee);
+ }
+ maxfee = std::max(maxfee, txfee);
+ minfee = std::min(minfee, txfee);
+ totalfee += txfee;
+
+ // New feerate uses satoshis per virtual byte instead of per serialized byte
+ CAmount feerate = weight ? (txfee * WITNESS_SCALE_FACTOR) / weight : 0;
+ if (do_medianfeerate) {
+ feerate_array.push_back(feerate);
+ }
+ maxfeerate = std::max(maxfeerate, feerate);
+ minfeerate = std::min(minfeerate, feerate);
+ }
+ }
+
+ UniValue ret_all(UniValue::VOBJ);
+ ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
+ ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
+ ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
+ ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex());
+ ret_all.pushKV("height", (int64_t)pindex->nHeight);
+ ret_all.pushKV("ins", inputs);
+ ret_all.pushKV("maxfee", maxfee);
+ ret_all.pushKV("maxfeerate", maxfeerate);
+ ret_all.pushKV("maxtxsize", maxtxsize);
+ ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
+ ret_all.pushKV("medianfeerate", CalculateTruncatedMedian(feerate_array));
+ ret_all.pushKV("mediantime", pindex->GetMedianTimePast());
+ ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
+ ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
+ ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
+ ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
+ ret_all.pushKV("outs", outputs);
+ ret_all.pushKV("subsidy", GetBlockSubsidy(pindex->nHeight, Params().GetConsensus()));
+ ret_all.pushKV("swtotal_size", swtotal_size);
+ ret_all.pushKV("swtotal_weight", swtotal_weight);
+ ret_all.pushKV("swtxs", swtxs);
+ ret_all.pushKV("time", pindex->GetBlockTime());
+ ret_all.pushKV("total_out", total_out);
+ ret_all.pushKV("total_size", total_size);
+ ret_all.pushKV("total_weight", total_weight);
+ ret_all.pushKV("totalfee", totalfee);
+ ret_all.pushKV("txs", (int64_t)block.vtx.size());
+ ret_all.pushKV("utxo_increase", outputs - inputs);
+ ret_all.pushKV("utxo_size_inc", utxo_size_inc);
+
+ if (do_all) {
+ return ret_all;
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ for (const std::string& stat : stats) {
+ const UniValue& value = ret_all[stat];
+ if (value.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic %s", stat));
+ }
+ ret.pushKV(stat, value);
+ }
+ return ret;
+}
+
+static UniValue savemempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
@@ -1637,11 +1924,296 @@ UniValue savemempool(const JSONRPCRequest& request)
return NullUniValue;
}
+//! Search for a given set of pubkey scripts
+bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
+ scan_progress = 0;
+ count = 0;
+ while (cursor->Valid()) {
+ COutPoint key;
+ Coin coin;
+ if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
+ if (++count % 8192 == 0) {
+ boost::this_thread::interruption_point();
+ if (should_abort) {
+ // allow to abort the scan via the abort reference
+ return false;
+ }
+ }
+ if (count % 256 == 0) {
+ // update progress reference every 256 item
+ uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
+ scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
+ }
+ if (needles.count(coin.out.scriptPubKey)) {
+ out_results.emplace(key, coin);
+ }
+ cursor->Next();
+ }
+ scan_progress = 100;
+ return true;
+}
+
+/** RAII object to prevent concurrency issue when scanning the txout set */
+static std::mutex g_utxosetscan;
+static std::atomic<int> g_scan_progress;
+static std::atomic<bool> g_scan_in_progress;
+static std::atomic<bool> g_should_abort_scan;
+class CoinsViewScanReserver
+{
+private:
+ bool m_could_reserve;
+public:
+ explicit CoinsViewScanReserver() : m_could_reserve(false) {}
+
+ bool reserve() {
+ assert (!m_could_reserve);
+ std::lock_guard<std::mutex> lock(g_utxosetscan);
+ if (g_scan_in_progress) {
+ return false;
+ }
+ g_scan_in_progress = true;
+ m_could_reserve = true;
+ return true;
+ }
+
+ ~CoinsViewScanReserver() {
+ if (m_could_reserve) {
+ std::lock_guard<std::mutex> lock(g_utxosetscan);
+ g_scan_in_progress = false;
+ }
+ }
+};
+
+static const char *g_default_scantxoutset_script_types[] = { "P2PKH", "P2SH_P2WPKH", "P2WPKH" };
+
+enum class OutputScriptType {
+ UNKNOWN,
+ P2PK,
+ P2PKH,
+ P2SH_P2WPKH,
+ P2WPKH
+};
+
+static inline OutputScriptType GetOutputScriptTypeFromString(const std::string& outputtype)
+{
+ if (outputtype == "P2PK") return OutputScriptType::P2PK;
+ else if (outputtype == "P2PKH") return OutputScriptType::P2PKH;
+ else if (outputtype == "P2SH_P2WPKH") return OutputScriptType::P2SH_P2WPKH;
+ else if (outputtype == "P2WPKH") return OutputScriptType::P2WPKH;
+ else return OutputScriptType::UNKNOWN;
+}
+
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputScriptType type)
+{
+ switch (type) {
+ case OutputScriptType::P2PKH: return key.GetID();
+ case OutputScriptType::P2SH_P2WPKH:
+ case OutputScriptType::P2WPKH: {
+ if (!key.IsCompressed()) return key.GetID();
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ if (type == OutputScriptType::P2SH_P2WPKH) {
+ CScript witprog = GetScriptForDestination(witdest);
+ return CScriptID(witprog);
+ } else {
+ return witdest;
+ }
+ }
+ default: assert(false);
+ }
+}
+
+UniValue scantxoutset(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw std::runtime_error(
+ "scantxoutset <action> ( <scanobjects> )\n"
+ "\nScans the unspent transaction output set for possible entries that matches common scripts of given public keys.\n"
+ "Using addresses as scanobjects will _not_ detect unspent P2PK txouts\n"
+ "\nArguments:\n"
+ "1. \"action\" (string, required) The action to execute\n"
+ " \"start\" for starting a scan\n"
+ " \"abort\" for aborting the current scan (returns true when abort was successful)\n"
+ " \"status\" for progress report (in %) of the current scan\n"
+ "2. \"scanobjects\" (array, optional) Array of scan objects (only one object type per scan object allowed)\n"
+ " [\n"
+ " { \"address\" : \"<address>\" }, (string, optional) Bitcoin address\n"
+ " { \"script\" : \"<scriptPubKey>\" }, (string, optional) HEX encoded script (scriptPubKey)\n"
+ " { \"pubkey\" : (object, optional) Public key\n"
+ " {\n"
+ " \"pubkey\" : \"<pubkey\">, (string, required) HEX encoded public key\n"
+ " \"script_types\" : [ ... ], (array, optional) Array of script-types to derive from the pubkey (possible values: \"P2PK\", \"P2PKH\", \"P2SH-P2WPKH\", \"P2WPKH\")\n"
+ " }\n"
+ " },\n"
+ " ]\n"
+ "\nResult:\n"
+ "{\n"
+ " \"unspents\": [\n"
+ " {\n"
+ " \"txid\" : \"transactionid\", (string) The transaction id\n"
+ " \"vout\": n, (numeric) the vout value\n"
+ " \"scriptPubKey\" : \"script\", (string) the script key\n"
+ " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
+ " \"height\" : n, (numeric) Height of the unspent transaction output\n"
+ " }\n"
+ " ,...], \n"
+ " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
+ "]\n"
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
+
+ UniValue result(UniValue::VOBJ);
+ if (request.params[0].get_str() == "status") {
+ CoinsViewScanReserver reserver;
+ if (reserver.reserve()) {
+ // no scan in progress
+ return NullUniValue;
+ }
+ result.pushKV("progress", g_scan_progress);
+ return result;
+ } else if (request.params[0].get_str() == "abort") {
+ CoinsViewScanReserver reserver;
+ if (reserver.reserve()) {
+ // reserve was possible which means no scan was running
+ return false;
+ }
+ // set the abort flag
+ g_should_abort_scan = true;
+ return true;
+ } else if (request.params[0].get_str() == "start") {
+ CoinsViewScanReserver reserver;
+ if (!reserver.reserve()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
+ }
+ std::set<CScript> needles;
+ CAmount total_in = 0;
+
+ // loop through the scan objects
+ for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
+ if (!scanobject.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scan object");
+ }
+ UniValue address_uni = find_value(scanobject, "address");
+ UniValue pubkey_uni = find_value(scanobject, "pubkey");
+ UniValue script_uni = find_value(scanobject, "script");
+
+ // make sure only one object type is present
+ if (1 != !address_uni.isNull() + !pubkey_uni.isNull() + !script_uni.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Only one object type is allowed per scan object");
+ } else if (!address_uni.isNull() && !address_uni.isStr()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"address\" must contain a single string as value");
+ } else if (!pubkey_uni.isNull() && !pubkey_uni.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"pubkey\" must contain an object as value");
+ } else if (!script_uni.isNull() && !script_uni.isStr()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"script\" must contain a single string as value");
+ } else if (address_uni.isStr()) {
+ // type: address
+ // decode destination and derive the scriptPubKey
+ // add the script to the scan containers
+ CTxDestination dest = DecodeDestination(address_uni.get_str());
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ }
+ CScript script = GetScriptForDestination(dest);
+ assert(!script.empty());
+ needles.insert(script);
+ } else if (pubkey_uni.isObject()) {
+ // type: pubkey
+ // derive script(s) according to the script_type parameter
+ UniValue script_types_uni = find_value(pubkey_uni, "script_types");
+ UniValue pubkeydata_uni = find_value(pubkey_uni, "pubkey");
+
+ // check the script types and use the default if not provided
+ if (!script_types_uni.isNull() && !script_types_uni.isArray()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "script_types must be an array");
+ } else if (script_types_uni.isNull()) {
+ // use the default script types
+ script_types_uni = UniValue(UniValue::VARR);
+ for (const char *t : g_default_scantxoutset_script_types) {
+ script_types_uni.push_back(t);
+ }
+ }
+
+ // check the acctual pubkey
+ if (!pubkeydata_uni.isStr() || !IsHex(pubkeydata_uni.get_str())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Public key must be hex encoded");
+ }
+ CPubKey pubkey(ParseHexV(pubkeydata_uni, "pubkey"));
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key");
+ }
+
+ // loop through the script types and derive the script
+ for (const UniValue& script_type_uni : script_types_uni.get_array().getValues()) {
+ OutputScriptType script_type = GetOutputScriptTypeFromString(script_type_uni.get_str());
+ if (script_type == OutputScriptType::UNKNOWN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script type");
+ CScript script;
+ if (script_type == OutputScriptType::P2PK) {
+ // support legacy P2PK scripts
+ script << ToByteVector(pubkey) << OP_CHECKSIG;
+ } else {
+ script = GetScriptForDestination(GetDestinationForKey(pubkey, script_type));
+ }
+ assert(!script.empty());
+ needles.insert(script);
+ }
+ } else if (script_uni.isStr()) {
+ // type: script
+ // check and add the script to the scan containers (needles array)
+ CScript script(ParseHexV(script_uni, "script"));
+ // TODO: check script: max length, has OP, is unspenable etc.
+ needles.insert(script);
+ }
+ }
+
+ // Scan the unspent transaction output set for inputs
+ UniValue unspents(UniValue::VARR);
+ std::vector<CTxOut> input_txos;
+ std::map<COutPoint, Coin> coins;
+ g_should_abort_scan = false;
+ g_scan_progress = 0;
+ int64_t count = 0;
+ std::unique_ptr<CCoinsViewCursor> pcursor;
+ {
+ LOCK(cs_main);
+ FlushStateToDisk();
+ pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
+ assert(pcursor);
+ }
+ bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
+ result.pushKV("success", res);
+ result.pushKV("searched_items", count);
+
+ for (const auto& it : coins) {
+ const COutPoint& outpoint = it.first;
+ const Coin& coin = it.second;
+ const CTxOut& txo = coin.out;
+ input_txos.push_back(txo);
+ total_in += txo.nValue;
+
+ UniValue unspent(UniValue::VOBJ);
+ unspent.pushKV("txid", outpoint.hash.GetHex());
+ unspent.pushKV("vout", (int32_t)outpoint.n);
+ unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("amount", ValueFromAmount(txo.nValue));
+ unspent.pushKV("height", (int32_t)coin.nHeight);
+
+ unspents.push_back(unspent);
+ }
+ result.pushKV("unspents", unspents);
+ result.pushKV("total_amount", ValueFromAmount(total_in));
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
+ }
+ return result;
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "blockchain", "getblockchaininfo", &getblockchaininfo, {} },
{ "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} },
+ { "blockchain", "getblockstats", &getblockstats, {"hash_or_height", "stats"} },
{ "blockchain", "getbestblockhash", &getbestblockhash, {} },
{ "blockchain", "getblockcount", &getblockcount, {} },
{ "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} },
@@ -1661,6 +2233,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
+ { "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 960edfd56f..3aa8de2d2b 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -16,7 +16,7 @@ class UniValue;
* @return A floating point number that is a multiple of the main net minimum
* difficulty (4295032833 hashes).
*/
-double GetDifficulty(const CBlockIndex* blockindex = nullptr);
+double GetDifficulty(const CBlockIndex* blockindex);
/** Callback for when block tip changed. */
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
@@ -34,4 +34,3 @@ UniValue mempoolToJSON(bool fVerbose = false);
UniValue blockheaderToJSON(const CBlockIndex* blockindex);
#endif
-
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 34c41b3b6b..eb6b164075 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -38,6 +38,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendtoaddress", 5 , "replaceable" },
{ "sendtoaddress", 6 , "conf_target" },
{ "settxfee", 0, "amount" },
+ { "sethdseed", 0, "newkeypool" },
{ "getreceivedbyaddress", 1, "minconf" },
{ "getreceivedbyaccount", 1, "minconf" },
{ "getreceivedbylabel", 1, "minconf" },
@@ -51,7 +52,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listreceivedbylabel", 0, "minconf" },
{ "listreceivedbylabel", 1, "include_empty" },
{ "listreceivedbylabel", 2, "include_watchonly" },
- { "getlabeladdress", 1, "force" },
{ "getbalance", 1, "minconf" },
{ "getbalance", 2, "include_watchonly" },
{ "getblockhash", 0, "height" },
@@ -78,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 4, "subtractfeefrom" },
{ "sendmany", 5 , "replaceable" },
{ "sendmany", 6 , "conf_target" },
+ { "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
{ "createmultisig", 0, "nrequired" },
@@ -109,6 +110,22 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
+ { "walletcreatefundedpsbt", 0, "inputs" },
+ { "walletcreatefundedpsbt", 1, "outputs" },
+ { "walletcreatefundedpsbt", 2, "locktime" },
+ { "walletcreatefundedpsbt", 3, "replaceable" },
+ { "walletcreatefundedpsbt", 4, "options" },
+ { "walletcreatefundedpsbt", 5, "bip32derivs" },
+ { "walletprocesspsbt", 1, "sign" },
+ { "walletprocesspsbt", 3, "bip32derivs" },
+ { "createpsbt", 0, "inputs" },
+ { "createpsbt", 1, "outputs" },
+ { "createpsbt", 2, "locktime" },
+ { "createpsbt", 3, "replaceable" },
+ { "combinepsbt", 0, "txs"},
+ { "finalizepsbt", 1, "extract"},
+ { "converttopsbt", 1, "permitsigdata"},
+ { "converttopsbt", 2, "iswitness"},
{ "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" },
{ "gettxoutproof", 0, "txids" },
@@ -122,6 +139,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "importmulti", 1, "options" },
{ "verifychain", 0, "checklevel" },
{ "verifychain", 1, "nblocks" },
+ { "getblockstats", 0, "hash_or_height" },
+ { "getblockstats", 1, "stats" },
{ "pruneblockchain", 0, "height" },
{ "keypoolrefill", 0, "newsize" },
{ "getrawmempool", 0, "verbose" },
@@ -153,6 +172,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "echojson", 9, "arg9" },
{ "rescanblockchain", 0, "start_height"},
{ "rescanblockchain", 1, "stop_height"},
+ { "createwallet", 1, "disable_private_keys"},
};
class CRPCConvertTable
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index b4bb78e689..e751587dc7 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,7 +10,6 @@
#include <consensus/params.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <init.h>
#include <validation.h>
#include <key_io.h>
#include <miner.h>
@@ -20,6 +19,7 @@
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/server.h>
+#include <shutdown.h>
#include <txmempool.h>
#include <util.h>
#include <utilstrencodings.h>
@@ -44,7 +44,7 @@ unsigned int ParseConfirmTarget(const UniValue& value)
* or from the last difficulty change if 'lookup' is nonpositive.
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
*/
-UniValue GetNetworkHashPS(int lookup, int height) {
+static UniValue GetNetworkHashPS(int lookup, int height) {
CBlockIndex *pb = chainActive.Tip();
if (height >= 0 && height < chainActive.Height())
@@ -81,7 +81,7 @@ UniValue GetNetworkHashPS(int lookup, int height) {
return workDiff.getdouble() / timeDiff;
}
-UniValue getnetworkhashps(const JSONRPCRequest& request)
+static UniValue getnetworkhashps(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
@@ -151,7 +151,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
return blockHashes;
}
-UniValue generatetoaddress(const JSONRPCRequest& request)
+static UniValue generatetoaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw std::runtime_error(
@@ -185,7 +185,7 @@ UniValue generatetoaddress(const JSONRPCRequest& request)
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
}
-UniValue getmininginfo(const JSONRPCRequest& request)
+static UniValue getmininginfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -214,7 +214,7 @@ UniValue getmininginfo(const JSONRPCRequest& request)
obj.pushKV("blocks", (int)chainActive.Height());
obj.pushKV("currentblockweight", (uint64_t)nLastBlockWeight);
obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx);
- obj.pushKV("difficulty", (double)GetDifficulty());
+ obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
obj.pushKV("networkhashps", getnetworkhashps(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
@@ -224,7 +224,7 @@ UniValue getmininginfo(const JSONRPCRequest& request)
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
-UniValue prioritisetransaction(const JSONRPCRequest& request)
+static UniValue prioritisetransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
@@ -278,7 +278,7 @@ static UniValue BIP22ValidationResult(const CValidationState& state)
return "valid?";
}
-std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
+static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
std::string s = vbinfo.name;
if (!vbinfo.gbt_force) {
@@ -287,7 +287,7 @@ std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
return s;
}
-UniValue getblocktemplate(const JSONRPCRequest& request)
+static UniValue getblocktemplate(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -337,7 +337,6 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
" \"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 SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n"
" \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n"
- " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
" }\n"
" ,...\n"
" ],\n"
@@ -525,6 +524,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew;
}
+ assert(pindexPrev);
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
const Consensus::Params& consensusParams = Params().GetConsensus();
@@ -694,7 +694,7 @@ protected:
}
};
-UniValue submitblock(const JSONRPCRequest& request)
+static UniValue submitblock(const JSONRPCRequest& request)
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
@@ -724,7 +724,6 @@ UniValue submitblock(const JSONRPCRequest& request)
}
uint256 hash = block.GetHash();
- bool fBlockPresent = false;
{
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(hash);
@@ -735,8 +734,6 @@ UniValue submitblock(const JSONRPCRequest& request)
if (pindex->nStatus & BLOCK_FAILED_MASK) {
return "duplicate-invalid";
}
- // Otherwise, we might only have the header - process the block before returning
- fBlockPresent = true;
}
}
@@ -748,13 +745,15 @@ UniValue submitblock(const JSONRPCRequest& request)
}
}
+ bool new_block;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
- bool fAccepted = ProcessNewBlock(Params(), blockptr, true, nullptr);
+ bool accepted = ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
UnregisterValidationInterface(&sc);
- if (fBlockPresent) {
- if (fAccepted && !sc.found) {
- return "duplicate-inconclusive";
+ if (!new_block) {
+ if (!accepted) {
+ // TODO Maybe pass down fNewBlock to AcceptBlockHeader, so it is properly set to true in this case?
+ return "invalid";
}
return "duplicate";
}
@@ -764,13 +763,13 @@ UniValue submitblock(const JSONRPCRequest& request)
return BIP22ValidationResult(sc.state);
}
-UniValue estimatefee(const JSONRPCRequest& request)
+static UniValue estimatefee(const JSONRPCRequest& request)
{
throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee was removed in v0.17.\n"
"Clients should use estimatesmartfee.");
}
-UniValue estimatesmartfee(const JSONRPCRequest& request)
+static UniValue estimatesmartfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -831,7 +830,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
return result;
}
-UniValue estimaterawfee(const JSONRPCRequest& request)
+static UniValue estimaterawfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 6754407dbd..09812bb980 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -7,12 +7,12 @@
#include <clientversion.h>
#include <core_io.h>
#include <crypto/ripemd160.h>
-#include <init.h>
#include <key_io.h>
#include <validation.h>
#include <httpserver.h>
#include <net.h>
#include <netbase.h>
+#include <outputtype.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
@@ -33,7 +33,7 @@
#include <univalue.h>
-UniValue validateaddress(const JSONRPCRequest& request)
+static UniValue validateaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -78,7 +78,7 @@ UniValue validateaddress(const JSONRPCRequest& request)
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));;
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
@@ -90,11 +90,11 @@ UniValue validateaddress(const JSONRPCRequest& request)
// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around
class CWallet;
-UniValue createmultisig(const JSONRPCRequest& request)
+static UniValue createmultisig(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
- std::string msg = "createmultisig nrequired [\"key\",...]\n"
+ std::string msg = "createmultisig nrequired [\"key\",...] ( \"address_type\" )\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"
@@ -104,6 +104,7 @@ UniValue createmultisig(const JSONRPCRequest& request)
" \"key\" (string) The hex-encoded public key\n"
" ,...\n"
" ]\n"
+ "3. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is legacy.\n"
"\nResult:\n"
"{\n"
@@ -134,18 +135,27 @@ UniValue createmultisig(const JSONRPCRequest& request)
}
}
+ // Get the output type
+ OutputType output_type = OutputType::LEGACY;
+ if (!request.params[2].isNull()) {
+ if (!ParseOutputType(request.params[2].get_str(), output_type)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
+ }
+ }
+
// Construct using pay-to-script-hash:
- CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- CScriptID innerID(inner);
+ const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
+ CBasicKeyStore keystore;
+ const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type);
UniValue result(UniValue::VOBJ);
- result.pushKV("address", EncodeDestination(innerID));
+ result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
}
-UniValue verifymessage(const JSONRPCRequest& request)
+static UniValue verifymessage(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
@@ -201,7 +211,7 @@ UniValue verifymessage(const JSONRPCRequest& request)
return (pubkey.GetID() == *keyID);
}
-UniValue signmessagewithprivkey(const JSONRPCRequest& request)
+static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
@@ -240,7 +250,7 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request)
return EncodeBase64(vchSig.data(), vchSig.size());
}
-UniValue setmocktime(const JSONRPCRequest& request)
+static UniValue setmocktime(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -299,7 +309,7 @@ static std::string RPCMallocInfo()
}
#endif
-UniValue getmemoryinfo(const JSONRPCRequest& request)
+static UniValue getmemoryinfo(const JSONRPCRequest& request)
{
/* Please, avoid using the word "pool" here in the RPC interface or help,
* as users will undoubtedly confuse it with the other "memory pool"
@@ -346,21 +356,22 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
}
}
-uint32_t getCategoryMask(UniValue cats) {
+static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
cats = cats.get_array();
- uint32_t mask = 0;
for (unsigned int i = 0; i < cats.size(); ++i) {
- uint32_t flag = 0;
std::string cat = cats[i].get_str();
- if (!GetLogCategory(&flag, &cat)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
+
+ bool success;
+ if (enable) {
+ success = g_logger->EnableCategory(cat);
+ } else {
+ success = g_logger->DisableCategory(cat);
}
- if (flag == BCLog::NONE) {
- return 0;
+
+ if (!success) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
}
- mask |= flag;
}
- return mask;
}
UniValue logging(const JSONRPCRequest& request)
@@ -399,25 +410,25 @@ UniValue logging(const JSONRPCRequest& request)
);
}
- uint32_t originalLogCategories = logCategories;
+ uint32_t original_log_categories = g_logger->GetCategoryMask();
if (request.params[0].isArray()) {
- logCategories |= getCategoryMask(request.params[0]);
+ EnableOrDisableLogCategories(request.params[0], true);
}
-
if (request.params[1].isArray()) {
- logCategories &= ~getCategoryMask(request.params[1]);
+ EnableOrDisableLogCategories(request.params[1], false);
}
+ uint32_t updated_log_categories = g_logger->GetCategoryMask();
+ uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
// Update libevent logging if BCLog::LIBEVENT has changed.
// If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
// in which case we should clear the BCLog::LIBEVENT flag.
// Throw an error if the user has explicitly asked to change only the libevent
// flag and it failed.
- uint32_t changedLogCategories = originalLogCategories ^ logCategories;
- if (changedLogCategories & BCLog::LIBEVENT) {
- if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
- logCategories &= ~BCLog::LIBEVENT;
- if (changedLogCategories == BCLog::LIBEVENT) {
+ if (changed_log_categories & BCLog::LIBEVENT) {
+ if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
+ g_logger->DisableCategory(BCLog::LIBEVENT);
+ if (changed_log_categories == BCLog::LIBEVENT) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
}
}
@@ -432,7 +443,7 @@ UniValue logging(const JSONRPCRequest& request)
return result;
}
-UniValue echo(const JSONRPCRequest& request)
+static UniValue echo(const JSONRPCRequest& request)
{
if (request.fHelp)
throw std::runtime_error(
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index fee2b765ba..8fa56e9335 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -23,7 +23,7 @@
#include <univalue.h>
-UniValue getconnectioncount(const JSONRPCRequest& request)
+static UniValue getconnectioncount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -42,7 +42,7 @@ UniValue getconnectioncount(const JSONRPCRequest& request)
return (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
}
-UniValue ping(const JSONRPCRequest& request)
+static UniValue ping(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -65,7 +65,7 @@ UniValue ping(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getpeerinfo(const JSONRPCRequest& request)
+static UniValue getpeerinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -190,7 +190,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
return ret;
}
-UniValue addnode(const JSONRPCRequest& request)
+static UniValue addnode(const JSONRPCRequest& request)
{
std::string strCommand;
if (!request.params[1].isNull())
@@ -237,7 +237,7 @@ UniValue addnode(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue disconnectnode(const JSONRPCRequest& request)
+static UniValue disconnectnode(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
throw std::runtime_error(
@@ -280,7 +280,7 @@ UniValue disconnectnode(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getaddednodeinfo(const JSONRPCRequest& request)
+static UniValue getaddednodeinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -347,7 +347,7 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request)
return ret;
}
-UniValue getnettotals(const JSONRPCRequest& request)
+static UniValue getnettotals(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
@@ -413,7 +413,7 @@ static UniValue GetNetworksInfo()
return networks;
}
-UniValue getnetworkinfo(const JSONRPCRequest& request)
+static UniValue getnetworkinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -475,7 +475,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
UniValue localAddresses(UniValue::VARR);
{
LOCK(cs_mapLocalHost);
- for (const std::pair<CNetAddr, LocalServiceInfo> &item : mapLocalHost)
+ for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
{
UniValue rec(UniValue::VOBJ);
rec.pushKV("address", item.first.ToString());
@@ -489,7 +489,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
return obj;
}
-UniValue setban(const JSONRPCRequest& request)
+static UniValue setban(const JSONRPCRequest& request)
{
std::string strCommand;
if (!request.params[1].isNull())
@@ -553,7 +553,7 @@ UniValue setban(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue listbanned(const JSONRPCRequest& request)
+static UniValue listbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -586,7 +586,7 @@ UniValue listbanned(const JSONRPCRequest& request)
return bannedAddresses;
}
-UniValue clearbanned(const JSONRPCRequest& request)
+static UniValue clearbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -604,7 +604,7 @@ UniValue clearbanned(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue setnetworkactive(const JSONRPCRequest& request)
+static UniValue setnetworkactive(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 4a265735d2..6954aed252 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -46,7 +46,6 @@ enum RPCErrorCode
//! 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
@@ -88,6 +87,9 @@ enum RPCErrorCode
//! Backwards compatible aliases
RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME,
+
+ //! Unused reserved codes, kept around for backwards compatibility. Do not reuse.
+ RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode
};
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 0cb34a0cca..bb94e11fea 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -5,10 +5,10 @@
#include <chain.h>
#include <coins.h>
+#include <compat/byteswap.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
-#include <init.h>
#include <keystore.h>
#include <validation.h>
#include <validationinterface.h>
@@ -19,7 +19,6 @@
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <rpc/rawtransaction.h>
-#include <rpc/safemode.h>
#include <rpc/server.h>
#include <script/script.h>
#include <script/script_error.h>
@@ -38,7 +37,7 @@
#include <univalue.h>
-void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
+static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
@@ -64,7 +63,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
}
}
-UniValue getrawtransaction(const JSONRPCRequest& request)
+static UniValue getrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
@@ -204,7 +203,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
return result;
}
-UniValue gettxoutproof(const JSONRPCRequest& request)
+static UniValue gettxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
throw std::runtime_error(
@@ -298,7 +297,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
return strHex;
}
-UniValue verifytxoutproof(const JSONRPCRequest& request)
+static UniValue verifytxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -308,7 +307,7 @@ UniValue verifytxoutproof(const JSONRPCRequest& request)
"\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"
+ "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
);
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
@@ -325,89 +324,39 @@ UniValue verifytxoutproof(const JSONRPCRequest& request)
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
- if (!pindex || !chainActive.Contains(pindex)) {
+ if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
- for (const uint256& hash : vMatch)
- res.push_back(hash.GetHex());
+ // Check if proof is valid, only add results if so
+ if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) {
+ for (const uint256& hash : vMatch) {
+ res.push_back(hash.GetHex());
+ }
+ }
+
return res;
}
-UniValue createrawtransaction(const JSONRPCRequest& request)
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
- throw std::runtime_error(
- // clang-format off
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
- "\nCreate a transaction spending the given inputs and creating new outputs.\n"
- "Outputs can be addresses or data.\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. \"inputs\" (array, 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. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
- " [\n"
- " {\n"
- " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
- " },\n"
- " {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
- " }\n"
- " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.\n"
- " ]\n"
- "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
- "\nResult:\n"
- "\"transaction\" (string) hex string of the transaction\n"
-
- "\nExamples:\n"
- + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
- + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
- + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
- + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
- // clang-format on
- );
- }
-
- RPCTypeCheck(request.params, {
- UniValue::VARR,
- UniValueType(), // ARR or OBJ, checked later
- UniValue::VNUM,
- UniValue::VBOOL
- }, true
- );
- if (request.params[0].isNull() || request.params[1].isNull())
+ if (inputs_in.isNull() || outputs_in.isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
- UniValue inputs = request.params[0].get_array();
- const bool outputs_is_obj = request.params[1].isObject();
- UniValue outputs = outputs_is_obj ?
- request.params[1].get_obj() :
- request.params[1].get_array();
+ UniValue inputs = inputs_in.get_array();
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
CMutableTransaction rawTx;
- if (!request.params[2].isNull()) {
- int64_t nLockTime = request.params[2].get_int64();
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.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;
}
- bool rbfOptIn = request.params[3].isTrue();
+ bool rbfOptIn = rbf.isTrue();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
@@ -487,14 +436,75 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
}
}
- if (!request.params[3].isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) {
+ if (!rbf.isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
}
+ return rawTx;
+}
+
+static UniValue createrawtransaction(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
+ throw std::runtime_error(
+ // clang-format off
+ "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
+ "\nCreate a transaction spending the given inputs and creating new outputs.\n"
+ "Outputs can be addresses or data.\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. \"inputs\" (array, 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. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ "\nResult:\n"
+ "\"transaction\" (string) hex string of the transaction\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ // clang-format on
+ );
+ }
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL
+ }, true
+ );
+
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+
return EncodeHexTx(rawTx);
}
-UniValue decoderawtransaction(const JSONRPCRequest& request)
+static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -570,7 +580,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
return result;
}
-UniValue decodescript(const JSONRPCRequest& request)
+static UniValue decodescript(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -638,9 +648,7 @@ UniValue decodescript(const JSONRPCRequest& request)
} else {
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
// Newer segwit program versions should be considered when then become available.
- uint256 scriptHash;
- CSHA256().Write(script.data(), script.size()).Finalize(scriptHash.begin());
- segwitScr = GetScriptForDestination(WitnessV0ScriptHash(scriptHash));
+ segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
ScriptPubKeyToUniv(segwitScr, sr, true);
sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
@@ -668,7 +676,7 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
-UniValue combinerawtransaction(const JSONRPCRequest& request)
+static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
@@ -737,19 +745,17 @@ UniValue combinerawtransaction(const JSONRPCRequest& request)
if (coin.IsSpent()) {
throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent");
}
- const CScript& prevPubKey = coin.out.scriptPubKey;
- const CAmount& amount = coin.out.nValue;
-
SignatureData sigdata;
// ... and merge in other signatures:
for (const CMutableTransaction& txv : txVariants) {
if (txv.vin.size() > i) {
- sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
+ sigdata.MergeSignatureData(DataFromTransaction(txv, i, coin.out));
}
}
+ ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&mergedTx, i, coin.out.nValue, 1), coin.out.scriptPubKey, sigdata);
- UpdateTransaction(mergedTx, i, sigdata);
+ UpdateInput(txin, sigdata);
}
return EncodeHexTx(mergedTx);
@@ -812,7 +818,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
Coin newcoin;
newcoin.out.scriptPubKey = scriptPubKey;
- newcoin.out.nValue = 0;
+ newcoin.out.nValue = MAX_MONEY;
if (prevOut.exists("amount")) {
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
}
@@ -839,23 +845,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
}
- int nHashType = SIGHASH_ALL;
- if (!hashType.isNull()) {
- static std::map<std::string, int> mapSigHashValues = {
- {std::string("ALL"), int(SIGHASH_ALL)},
- {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
- {std::string("NONE"), int(SIGHASH_NONE)},
- {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
- {std::string("SINGLE"), int(SIGHASH_SINGLE)},
- {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
- };
- std::string strHashType = hashType.get_str();
- if (mapSigHashValues.count(strHashType)) {
- nHashType = mapSigHashValues[strHashType];
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
- }
- }
+ int nHashType = ParseSighashString(hashType);
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
@@ -876,14 +866,18 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
const CScript& prevPubKey = coin.out.scriptPubKey;
const CAmount& amount = coin.out.nValue;
- SignatureData sigdata;
+ SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
}
- sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i));
- UpdateTransaction(mtx, i, sigdata);
+ UpdateInput(txin, sigdata);
+
+ // amount must be specified for valid segwit signature
+ if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin.out.ToString()));
+ }
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
@@ -907,7 +901,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
return result;
}
-UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
+static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
@@ -989,7 +983,8 @@ UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
UniValue signrawtransaction(const JSONRPCRequest& request)
{
#ifdef ENABLE_WALLET
- CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
#endif
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
@@ -1085,7 +1080,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
}
}
-UniValue sendrawtransaction(const JSONRPCRequest& request)
+static UniValue sendrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -1108,8 +1103,6 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
- ObserveSafeMode();
-
std::promise<void> promise;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
@@ -1182,7 +1175,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
return hashTx.GetHex();
}
-UniValue testmempoolaccept(const JSONRPCRequest& request)
+static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
@@ -1217,8 +1210,6 @@ UniValue testmempoolaccept(const JSONRPCRequest& request)
);
}
- ObserveSafeMode();
-
RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
if (request.params[0].get_array().size() != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
@@ -1263,6 +1254,553 @@ UniValue testmempoolaccept(const JSONRPCRequest& request)
return result;
}
+static std::string WriteHDKeypath(std::vector<uint32_t>& keypath)
+{
+ std::string keypath_str = "m";
+ for (uint32_t num : keypath) {
+ keypath_str += "/";
+ bool hardened = false;
+ if (num & 0x80000000) {
+ hardened = true;
+ num &= ~0x80000000;
+ }
+
+ keypath_str += std::to_string(num);
+ if (hardened) {
+ keypath_str += "'";
+ }
+ }
+ return keypath_str;
+}
+
+UniValue decodepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "decodepsbt \"psbt\"\n"
+ "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n"
+
+ "\nArguments:\n"
+ "1. \"psbt\" (string, required) The PSBT base64 string\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n"
+ " ... The layout is the same as the output of decoderawtransaction.\n"
+ " },\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " \"inputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"non_witness_utxo\" : { (json object, optional) Decoded network transaction for non-witness UTXOs\n"
+ " ...\n"
+ " },\n"
+ " \"witness_utxo\" : { (json object, optional) Transaction output for witness UTXOs\n"
+ " \"amount\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
+ " \"scriptPubKey\" : { (json object)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " \"address\" : \"address\" (string) Bitcoin address if there is one\n"
+ " }\n"
+ " },\n"
+ " \"partial_signatures\" : { (json object, optional)\n"
+ " \"pubkey\" : \"signature\", (string) The public key and signature that corresponds to it.\n"
+ " ,...\n"
+ " }\n"
+ " \"sighash\" : \"type\", (string, optional) The sighash type to be used\n"
+ " \"redeem_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"witness_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"bip32_derivs\" : { (json object, optional)\n"
+ " \"pubkey\" : { (json object, optional) The public key with the derivation path as the value.\n"
+ " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
+ " \"path\" : \"path\", (string) The path\n"
+ " }\n"
+ " ,...\n"
+ " }\n"
+ " \"final_scriptsig\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " }\n"
+ " \"final_scriptwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"outputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"redeem_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"witness_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"bip32_derivs\" : [ (array of json objects, optional)\n"
+ " {\n"
+ " \"pubkey\" : \"pubkey\", (string) The public key this path corresponds to\n"
+ " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
+ " \"path\" : \"path\", (string) The path\n"
+ " }\n"
+ " }\n"
+ " ,...\n"
+ " ],\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"fee\" : fee (numeric, optional) The transaction fee paid if all UTXOs slots in the PSBT have been filled.\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("decodepsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ UniValue result(UniValue::VOBJ);
+
+ // Add the decoded tx
+ UniValue tx_univ(UniValue::VOBJ);
+ TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false);
+ result.pushKV("tx", tx_univ);
+
+ // Unknown data
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : psbtx.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ result.pushKV("unknown", unknowns);
+
+ // inputs
+ CAmount total_in = 0;
+ bool have_all_utxos = true;
+ UniValue inputs(UniValue::VARR);
+ for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
+ const PSBTInput& input = psbtx.inputs[i];
+ UniValue in(UniValue::VOBJ);
+ // UTXOs
+ if (!input.witness_utxo.IsNull()) {
+ const CTxOut& txout = input.witness_utxo;
+
+ UniValue out(UniValue::VOBJ);
+
+ out.pushKV("amount", ValueFromAmount(txout.nValue));
+ total_in += txout.nValue;
+
+ UniValue o(UniValue::VOBJ);
+ ScriptToUniv(txout.scriptPubKey, o, true);
+ out.pushKV("scriptPubKey", o);
+ in.pushKV("witness_utxo", out);
+ } else if (input.non_witness_utxo) {
+ UniValue non_wit(UniValue::VOBJ);
+ TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
+ in.pushKV("non_witness_utxo", non_wit);
+ total_in += input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue;
+ } else {
+ have_all_utxos = false;
+ }
+
+ // Partial sigs
+ if (!input.partial_sigs.empty()) {
+ UniValue partial_sigs(UniValue::VOBJ);
+ for (const auto& sig : input.partial_sigs) {
+ partial_sigs.pushKV(HexStr(sig.second.first), HexStr(sig.second.second));
+ }
+ in.pushKV("partial_signatures", partial_sigs);
+ }
+
+ // Sighash
+ if (input.sighash_type > 0) {
+ in.pushKV("sighash", SighashToStr((unsigned char)input.sighash_type));
+ }
+
+ // Redeem script and witness script
+ if (!input.redeem_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(input.redeem_script, r, false);
+ in.pushKV("redeem_script", r);
+ }
+ if (!input.witness_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(input.witness_script, r, false);
+ in.pushKV("witness_script", r);
+ }
+
+ // keypaths
+ if (!input.hd_keypaths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (auto entry : input.hd_keypaths) {
+ UniValue keypath(UniValue::VOBJ);
+ keypath.pushKV("pubkey", HexStr(entry.first));
+
+ uint32_t fingerprint = entry.second.at(0);
+ keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint)));
+
+ entry.second.erase(entry.second.begin());
+ keypath.pushKV("path", WriteHDKeypath(entry.second));
+ keypaths.push_back(keypath);
+ }
+ in.pushKV("bip32_derivs", keypaths);
+ }
+
+ // Final scriptSig and scriptwitness
+ if (!input.final_script_sig.empty()) {
+ UniValue scriptsig(UniValue::VOBJ);
+ scriptsig.pushKV("asm", ScriptToAsmStr(input.final_script_sig, true));
+ scriptsig.pushKV("hex", HexStr(input.final_script_sig));
+ in.pushKV("final_scriptSig", scriptsig);
+ }
+ if (!input.final_script_witness.IsNull()) {
+ UniValue txinwitness(UniValue::VARR);
+ for (const auto& item : input.final_script_witness.stack) {
+ txinwitness.push_back(HexStr(item.begin(), item.end()));
+ }
+ in.pushKV("final_scriptwitness", txinwitness);
+ }
+
+ // Unknown data
+ if (input.unknown.size() > 0) {
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : input.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ in.pushKV("unknown", unknowns);
+ }
+
+ inputs.push_back(in);
+ }
+ result.pushKV("inputs", inputs);
+
+ // outputs
+ CAmount output_value = 0;
+ UniValue outputs(UniValue::VARR);
+ for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) {
+ const PSBTOutput& output = psbtx.outputs[i];
+ UniValue out(UniValue::VOBJ);
+ // Redeem script and witness script
+ if (!output.redeem_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(output.redeem_script, r, false);
+ out.pushKV("redeem_script", r);
+ }
+ if (!output.witness_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(output.witness_script, r, false);
+ out.pushKV("witness_script", r);
+ }
+
+ // keypaths
+ if (!output.hd_keypaths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (auto entry : output.hd_keypaths) {
+ UniValue keypath(UniValue::VOBJ);
+ keypath.pushKV("pubkey", HexStr(entry.first));
+
+ uint32_t fingerprint = entry.second.at(0);
+ keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint)));
+
+ entry.second.erase(entry.second.begin());
+ keypath.pushKV("path", WriteHDKeypath(entry.second));
+ keypaths.push_back(keypath);
+ }
+ out.pushKV("bip32_derivs", keypaths);
+ }
+
+ // Unknown data
+ if (output.unknown.size() > 0) {
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : output.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ out.pushKV("unknown", unknowns);
+ }
+
+ outputs.push_back(out);
+
+ // Fee calculation
+ output_value += psbtx.tx->vout[i].nValue;
+ }
+ result.pushKV("outputs", outputs);
+ if (have_all_utxos) {
+ result.pushKV("fee", ValueFromAmount(total_in - output_value));
+ }
+
+ return result;
+}
+
+UniValue combinepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "combinepsbt [\"psbt\",...]\n"
+ "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
+ "Implements the Combiner role.\n"
+ "\nArguments:\n"
+ "1. \"txs\" (string) A json array of base64 strings of partially signed transactions\n"
+ " [\n"
+ " \"psbt\" (string) A base64 string of a PSBT\n"
+ " ,...\n"
+ " ]\n"
+
+ "\nResult:\n"
+ " \"psbt\" (string) The base64-encoded partially signed transaction\n"
+ "\nExamples:\n"
+ + HelpExampleCli("combinepsbt", "[\"mybase64_1\", \"mybase64_2\", \"mybase64_3\"]")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VARR}, true);
+
+ // Unserialize the transactions
+ std::vector<PartiallySignedTransaction> psbtxs;
+ UniValue txs = request.params[0].get_array();
+ for (unsigned int i = 0; i < txs.size(); ++i) {
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, txs[i].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+ psbtxs.push_back(psbtx);
+ }
+
+ PartiallySignedTransaction merged_psbt(psbtxs[0]); // Copy the first one
+
+ // Merge
+ for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
+ if (*it != merged_psbt) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "PSBTs do not refer to the same transactions.");
+ }
+ merged_psbt.Merge(*it);
+ }
+ if (!merged_psbt.IsSane()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Merged PSBT is inconsistent");
+ }
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << merged_psbt;
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue finalizepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw std::runtime_error(
+ "finalizepsbt \"psbt\" ( extract )\n"
+ "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
+ "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
+ "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
+ "Implements the Finalizer and Extractor roles.\n"
+ "\nArguments:\n"
+ "1. \"psbt\" (string) A base64 string of a PSBT\n"
+ "2. \"extract\" (boolean, optional, default=true) If true and the transaction is complete, \n"
+ " extract and return the complete transaction in normal network serialization instead of the PSBT.\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n"
+ " \"hex\" : \"value\", (string) The hex-encoded network transaction if extracted\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("finalizepsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Get all of the previous transactions
+ bool complete = true;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ SignatureData sigdata;
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, sigdata, i, 1);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool());
+ if (complete && extract) {
+ CMutableTransaction mtx(*psbtx.tx);
+ for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
+ mtx.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
+ mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
+ }
+ ssTx << mtx;
+ result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
+ } else {
+ ssTx << psbtx;
+ result.push_back(Pair("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size())));
+ }
+ result.push_back(Pair("complete", complete));
+
+ return result;
+}
+
+UniValue createpsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
+ throw std::runtime_error(
+ "createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
+ "\nCreates a transaction in the Partially Signed Transaction format.\n"
+ "Implements the Creator role.\n"
+ "\nArguments:\n"
+ "1. \"inputs\" (array, 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. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ "\nResult:\n"
+ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ "\nExamples:\n"
+ + HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ );
+
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL,
+ }, true
+ );
+
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = rawTx;
+ for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue converttopsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
+ throw std::runtime_error(
+ "converttopsbt \"hexstring\" ( permitsigdata iswitness )\n"
+ "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
+ "createpsbt and walletcreatefundedpsbt should be used for new applications.\n"
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The hex string of a raw transaction\n"
+ "2. permitsigdata (boolean, optional, default=false) If true, any signatures in the input will be discarded and conversion.\n"
+ " will continue. If false, RPC will fail if any signatures are present.\n"
+ "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction.\n"
+ " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n"
+ " will be tried. If false, only non-witness deserialization wil be tried. Only has an effect if\n"
+ " permitsigdata is true.\n"
+ "\nResult:\n"
+ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ "\nExamples:\n"
+ "\nCreate a transaction\n"
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") +
+ "\nConvert the transaction to a PSBT\n"
+ + HelpExampleCli("converttopsbt", "\"rawtransaction\"")
+ );
+
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
+
+ // parse hex string from parameter
+ CMutableTransaction tx;
+ bool permitsigdata = request.params[1].isNull() ? false : request.params[1].get_bool();
+ bool witness_specified = !request.params[2].isNull();
+ bool iswitness = witness_specified ? request.params[2].get_bool() : false;
+ bool try_witness = permitsigdata ? (witness_specified ? iswitness : true) : false;
+ bool try_no_witness = permitsigdata ? (witness_specified ? !iswitness : true) : true;
+ if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ // Remove all scriptSigs and scriptWitnesses from inputs
+ for (CTxIn& input : tx.vin) {
+ if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptSigs and scriptWitnesses");
+ }
+ input.scriptSig.clear();
+ input.scriptWitness.SetNull();
+ }
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = tx;
+ for (unsigned int i = 0; i < tx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < tx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1275,6 +1813,11 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
+ { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} },
+ { "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} },
+ { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
+ { "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
+ { "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
index ec9d1f2cf0..52dccc90e8 100644
--- a/src/rpc/rawtransaction.h
+++ b/src/rpc/rawtransaction.h
@@ -12,4 +12,7 @@ class UniValue;
/** Sign a transaction with the given keystore and previous transactions */
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
+/** Create a transaction from univalue parameters */
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+
#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp
deleted file mode 100644
index 9f3a9d30b8..0000000000
--- a/src/rpc/safemode.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <rpc/safemode.h>
-
-#include <rpc/protocol.h>
-#include <util.h>
-#include <warnings.h>
-
-void ObserveSafeMode()
-{
- std::string warning = GetWarnings("rpc");
- if (warning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE)) {
- throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + warning);
- }
-}
-
diff --git a/src/rpc/safemode.h b/src/rpc/safemode.h
deleted file mode 100644
index 8466d6b2f9..0000000000
--- a/src/rpc/safemode.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2017 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_RPC_SAFEMODE_H
-#define BITCOIN_RPC_SAFEMODE_H
-
-static const bool DEFAULT_DISABLE_SAFEMODE = true;
-
-void ObserveSafeMode();
-
-#endif // BITCOIN_RPC_SAFEMODE_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index c7c3b1f0d3..b420e9d8b3 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -1,14 +1,14 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 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 <fs.h>
-#include <init.h>
#include <key_io.h>
#include <random.h>
+#include <shutdown.h>
#include <sync.h>
#include <ui_interface.h>
#include <util.h>
@@ -23,10 +23,10 @@
#include <memory> // for unique_ptr
#include <unordered_map>
-static bool fRPCRunning = false;
-static bool fRPCInWarmup = true;
-static std::string rpcWarmupStatus("RPC server started");
static CCriticalSection cs_rpcWarmup;
+static bool fRPCRunning = false;
+static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true;
+static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started";
/* Timer-creating functions */
static RPCTimerInterface* timerInterface = nullptr;
/* Map of name to timer. */
@@ -239,7 +239,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
return "Bitcoin server stopping";
}
-UniValue uptime(const JSONRPCRequest& jsonRequest)
+static UniValue uptime(const JSONRPCRequest& jsonRequest)
{
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(