diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a7e364e0fb..23a3c6b1ae 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1,1858 +1,1860 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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/blockchain.h" #include "amount.h" #include "chain.h" #include "chainparams.h" #include "checkpoints.h" #include "coins.h" #include "config.h" #include "consensus/validation.h" #include "core_io.h" #include "hash.h" #include "policy/policy.h" #include "primitives/transaction.h" #include "rpc/server.h" #include "streams.h" #include "sync.h" #include "txdb.h" #include "txmempool.h" #include "util.h" #include "utilstrencodings.h" #include "validation.h" #include // boost::thread::interrupt #include #include #include struct CUpdatedBlock { uint256 hash; int height; }; static CWaitableCriticalSection cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; static double GetDifficultyFromBits(uint32_t nBits) { int nShift = (nBits >> 24) & 0xff; double dDiff = 0x0000ffff / double(nBits & 0x00ffffff); while (nShift < 29) { dDiff *= 256.0; nShift++; } while (nShift > 29) { dDiff /= 256.0; nShift--; } return dDiff; } double GetDifficulty(const CBlockIndex *blockindex) { // Floating point number that is a multiple of the minimum difficulty, // minimum difficulty = 1.0. if (blockindex == nullptr) { return 1.0; } return GetDifficultyFromBits(blockindex->nBits); } UniValue blockheaderToJSON(const CBlockIndex *blockindex) { AssertLockHeld(cs_main); UniValue result(UniValue::VOBJ); result.pushKV("hash", blockindex->GetBlockHash().GetHex()); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) { confirmations = chainActive.Height() - blockindex->nHeight + 1; } result.pushKV("confirmations", confirmations); result.pushKV("height", blockindex->nHeight); result.pushKV("version", blockindex->nVersion); result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion)); result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex()); result.pushKV("time", int64_t(blockindex->nTime)); result.pushKV("mediantime", int64_t(blockindex->GetMedianTimePast())); result.pushKV("nonce", uint64_t(blockindex->nNonce)); result.pushKV("bits", strprintf("%08x", blockindex->nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) { result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); } CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) { result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); } return result; } UniValue blockToJSON(const Config &config, const CBlock &block, const CBlockIndex *blockindex, bool txDetails) { AssertLockHeld(cs_main); UniValue result(UniValue::VOBJ); result.pushKV("hash", blockindex->GetBlockHash().GetHex()); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) { confirmations = chainActive.Height() - blockindex->nHeight + 1; } result.pushKV("confirmations", confirmations); result.pushKV( "size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); result.pushKV("height", blockindex->nHeight); result.pushKV("version", block.nVersion); result.pushKV("versionHex", strprintf("%08x", block.nVersion)); result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); UniValue txs(UniValue::VARR); for (const auto &tx : block.vtx) { if (txDetails) { UniValue objTx(UniValue::VOBJ); TxToUniv(*tx, uint256(), objTx); txs.push_back(objTx); } else { txs.push_back(tx->GetId().GetHex()); } } result.pushKV("tx", txs); result.pushKV("time", block.GetBlockTime()); result.pushKV("mediantime", int64_t(blockindex->GetMedianTimePast())); result.pushKV("nonce", uint64_t(block.nNonce)); result.pushKV("bits", strprintf("%08x", block.nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) { result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); } CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) { result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); } return result; } UniValue getblockcount(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getblockcount\n" "\nReturns the number of blocks in the longest blockchain.\n" "\nResult:\n" "n (numeric) The current block count\n" "\nExamples:\n" + HelpExampleCli("getblockcount", "") + HelpExampleRpc("getblockcount", "")); } LOCK(cs_main); return chainActive.Height(); } UniValue getbestblockhash(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getbestblockhash\n" "\nReturns the hash of the best (tip) block in the " "longest blockchain.\n" "\nResult:\n" "\"hex\" (string) the block hash hex encoded\n" "\nExamples:\n" + HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "")); } LOCK(cs_main); return chainActive.Tip()->GetBlockHash().GetHex(); } UniValue getfinalizedblockhash(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getfinalizedblockhash\n" "\nReturns the hash of the currently finalized block\n" "\nResult:\n" "\"hex\" (string) the block hash hex encoded\n"); } LOCK(cs_main); const CBlockIndex *blockIndexFinalized = GetFinalizedBlock(); if (blockIndexFinalized) { return blockIndexFinalized->GetBlockHash().GetHex(); } return UniValue(UniValue::VSTR); } void RPCNotifyBlockChange(bool ibd, const CBlockIndex *pindex) { if (pindex) { std::lock_guard lock(cs_blockchange); latestblock.hash = pindex->GetBlockHash(); latestblock.height = pindex->nHeight; } cond_blockchange.notify_all(); } UniValue waitfornewblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "waitfornewblock (timeout)\n" "\nWaits for a specific new block and returns " "useful info about it.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. timeout (int, optional, default=0) Time in " "milliseconds to wait for a response. 0 indicates " "no timeout.\n" "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" "\nExamples:\n" + HelpExampleCli("waitfornewblock", "1000") + HelpExampleRpc("waitfornewblock", "1000")); } int timeout = 0; - if (request.params.size() > 0) { + if (!request.params[0].isNull()) { timeout = request.params[0].get_int(); } CUpdatedBlock block; { WAIT_LOCK(cs_blockchange, lock); block = latestblock; if (timeout) { cond_blockchange.wait_for( lock, std::chrono::milliseconds(timeout), [&block] { return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); } else { cond_blockchange.wait(lock, [&block] { return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); } block = latestblock; } UniValue ret(UniValue::VOBJ); ret.pushKV("hash", block.hash.GetHex()); ret.pushKV("height", block.height); return ret; } UniValue waitforblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "waitforblock (timeout)\n" "\nWaits for a specific new block and returns useful info about " "it.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. \"blockhash\" (required, string) Block hash to wait for.\n" "2. timeout (int, optional, default=0) Time in milliseconds " "to wait for a response. 0 indicates no timeout.\n" "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" "\nExamples:\n" + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4" "570b24c9ed7b4a8c619eb02596f8862\", " "1000") + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4" "570b24c9ed7b4a8c619eb02596f8862\", " "1000")); } int timeout = 0; uint256 hash = uint256S(request.params[0].get_str()); - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { timeout = request.params[1].get_int(); } CUpdatedBlock block; { WAIT_LOCK(cs_blockchange, lock); if (timeout) { cond_blockchange.wait_for( lock, std::chrono::milliseconds(timeout), [&hash] { return latestblock.hash == hash || !IsRPCRunning(); }); } else { cond_blockchange.wait(lock, [&hash] { return latestblock.hash == hash || !IsRPCRunning(); }); } block = latestblock; } UniValue ret(UniValue::VOBJ); ret.pushKV("hash", block.hash.GetHex()); ret.pushKV("height", block.height); return ret; } UniValue waitforblockheight(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "waitforblockheight (timeout)\n" "\nWaits for (at least) block height and returns the height and " "hash\n" "of the current tip.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. height (required, int) Block height to wait for (int)\n" "2. timeout (int, optional, default=0) Time in milliseconds to " "wait for a response. 0 indicates no timeout.\n" "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" "\nExamples:\n" + HelpExampleCli("waitforblockheight", "\"100\", 1000") + HelpExampleRpc("waitforblockheight", "\"100\", 1000")); } int timeout = 0; int height = request.params[0].get_int(); - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { timeout = request.params[1].get_int(); } CUpdatedBlock block; { WAIT_LOCK(cs_blockchange, lock); if (timeout) { cond_blockchange.wait_for( lock, std::chrono::milliseconds(timeout), [&height] { return latestblock.height >= height || !IsRPCRunning(); }); } else { cond_blockchange.wait(lock, [&height] { return latestblock.height >= height || !IsRPCRunning(); }); } block = latestblock; } UniValue ret(UniValue::VOBJ); ret.pushKV("hash", block.hash.GetHex()); ret.pushKV("height", block.height); return ret; } UniValue getdifficulty(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error("getdifficulty\n" "\nReturns the proof-of-work difficulty as a " "multiple of the minimum difficulty.\n" "\nResult:\n" "n.nnn (numeric) the proof-of-work " "difficulty as a multiple of the minimum " "difficulty.\n" "\nExamples:\n" + HelpExampleCli("getdifficulty", "") + HelpExampleRpc("getdifficulty", "")); } LOCK(cs_main); return GetDifficulty(chainActive.Tip()); } std::string EntryDescriptionString() { return " \"size\" : n, (numeric) transaction size.\n" " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" " \"modifiedfee\" : n, (numeric) transaction fee with fee " "deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction " "entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when " "transaction entered pool\n" " \"startingpriority\" : n, (numeric) DEPRECATED. Priority when " "transaction entered pool\n" " \"currentpriority\" : n, (numeric) DEPRECATED. Transaction " "priority now\n" " \"descendantcount\" : n, (numeric) number of in-mempool " "descendant transactions (including this one)\n" " \"descendantsize\" : n, (numeric) virtual transaction size " "of in-mempool descendants (including this one)\n" " \"descendantfees\" : n, (numeric) modified fees (see above) " "of in-mempool descendants (including this one)\n" " \"ancestorcount\" : n, (numeric) number of in-mempool " "ancestor transactions (including this one)\n" " \"ancestorsize\" : n, (numeric) virtual transaction size " "of in-mempool ancestors (including this one)\n" " \"ancestorfees\" : n, (numeric) modified fees (see above) " "of in-mempool ancestors (including this one)\n" " \"depends\" : [ (array) unconfirmed transactions " "used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n"; } void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) { AssertLockHeld(g_mempool.cs); info.pushKV("size", (int)e.GetTxSize()); info.pushKV("fee", ValueFromAmount(e.GetFee())); info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); info.pushKV("time", e.GetTime()); info.pushKV("height", (int)e.GetHeight()); info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); info.pushKV("currentpriority", e.GetPriority(chainActive.Height())); info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); info.pushKV("descendantfees", e.GetModFeesWithDescendants() / SATOSHI); info.pushKV("ancestorcount", e.GetCountWithAncestors()); info.pushKV("ancestorsize", e.GetSizeWithAncestors()); info.pushKV("ancestorfees", e.GetModFeesWithAncestors() / SATOSHI); const CTransaction &tx = e.GetTx(); std::set setDepends; for (const CTxIn &txin : tx.vin) { if (g_mempool.exists(txin.prevout.GetTxId())) { setDepends.insert(txin.prevout.GetTxId().ToString()); } } UniValue depends(UniValue::VARR); for (const std::string &dep : setDepends) { depends.push_back(dep); } info.pushKV("depends", depends); } UniValue mempoolToJSON(bool fVerbose = false) { if (fVerbose) { LOCK(g_mempool.cs); UniValue o(UniValue::VOBJ); for (const CTxMemPoolEntry &e : g_mempool.mapTx) { const uint256 &txid = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); o.pushKV(txid.ToString(), info); } return o; } else { std::vector vtxids; g_mempool.queryHashes(vtxids); UniValue a(UniValue::VARR); for (const uint256 &txid : vtxids) { a.push_back(txid.ToString()); } return a; } } UniValue getrawmempool(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getrawmempool ( verbose )\n" "\nReturns all transaction ids in memory pool as a json array of " "string transaction ids.\n" "\nArguments:\n" "1. verbose (boolean, optional, default=false) True for a json " "object, false for array of transaction ids\n" "\nResult: (for verbose = false):\n" "[ (json array of string)\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" "]\n" "\nResult: (for verbose = true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" "\nExamples:\n" + HelpExampleCli("getrawmempool", "true") + HelpExampleRpc("getrawmempool", "true")); } bool fVerbose = false; - if (request.params.size() > 0) { + if (!request.params[0].isNull()) { fVerbose = request.params[0].get_bool(); } return mempoolToJSON(fVerbose); } UniValue getmempoolancestors(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getmempoolancestors txid (verbose)\n" "\nIf txid is in the mempool, returns all in-mempool ancestors.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id " "(must be in mempool)\n" "2. verbose (boolean, optional, default=false) " "True for a json object, false for array of transaction ids\n" "\nResult (for verbose=false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an " "in-mempool ancestor transaction\n" " ,...\n" "]\n" "\nResult (for verbose=true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolancestors", "\"mytxid\"") + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")); } bool fVerbose = false; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { fVerbose = request.params[1].get_bool(); } uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(g_mempool.cs); CTxMemPool::txiter it = g_mempool.mapTx.find(hash); if (it == g_mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } CTxMemPool::setEntries setAncestors; uint64_t noLimit = std::numeric_limits::max(); std::string dummy; g_mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false); if (!fVerbose) { UniValue o(UniValue::VARR); for (CTxMemPool::txiter ancestorIt : setAncestors) { o.push_back(ancestorIt->GetTx().GetId().ToString()); } return o; } else { UniValue o(UniValue::VOBJ); for (CTxMemPool::txiter ancestorIt : setAncestors) { const CTxMemPoolEntry &e = *ancestorIt; const uint256 &_hash = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); o.pushKV(_hash.ToString(), info); } return o; } } UniValue getmempooldescendants(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getmempooldescendants txid (verbose)\n" "\nIf txid is in the mempool, returns all in-mempool descendants.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id " "(must be in mempool)\n" "2. verbose (boolean, optional, default=false) " "True for a json object, false for array of transaction ids\n" "\nResult (for verbose=false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an " "in-mempool descendant transaction\n" " ,...\n" "]\n" "\nResult (for verbose=true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempooldescendants", "\"mytxid\"") + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")); } bool fVerbose = false; - if (request.params.size() > 1) fVerbose = request.params[1].get_bool(); + if (!request.params[1].isNull()) { + fVerbose = request.params[1].get_bool(); + } uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(g_mempool.cs); CTxMemPool::txiter it = g_mempool.mapTx.find(hash); if (it == g_mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } CTxMemPool::setEntries setDescendants; g_mempool.CalculateDescendants(it, setDescendants); // CTxMemPool::CalculateDescendants will include the given tx setDescendants.erase(it); if (!fVerbose) { UniValue o(UniValue::VARR); for (CTxMemPool::txiter descendantIt : setDescendants) { o.push_back(descendantIt->GetTx().GetId().ToString()); } return o; } else { UniValue o(UniValue::VOBJ); for (CTxMemPool::txiter descendantIt : setDescendants) { const CTxMemPoolEntry &e = *descendantIt; const uint256 &_hash = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); o.pushKV(_hash.ToString(), info); } return o; } } UniValue getmempoolentry(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getmempoolentry txid\n" "\nReturns mempool data for given transaction\n" "\nArguments:\n" "1. \"txid\" (string, required) " "The transaction id (must be in mempool)\n" "\nResult:\n" "{ (json object)\n" + EntryDescriptionString() + "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolentry", "\"mytxid\"") + HelpExampleRpc("getmempoolentry", "\"mytxid\"")); } uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(g_mempool.cs); CTxMemPool::txiter it = g_mempool.mapTx.find(hash); if (it == g_mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } const CTxMemPoolEntry &e = *it; UniValue info(UniValue::VOBJ); entryToJSON(info, e); return info; } UniValue getblockhash(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getblockhash height\n" "\nReturns hash of block in best-block-chain at height provided.\n" "\nArguments:\n" "1. height (numeric, required) The height index\n" "\nResult:\n" "\"hash\" (string) The block hash\n" "\nExamples:\n" + HelpExampleCli("getblockhash", "1000") + HelpExampleRpc("getblockhash", "1000")); } LOCK(cs_main); int nHeight = request.params[0].get_int(); if (nHeight < 0 || nHeight > chainActive.Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } CBlockIndex *pblockindex = chainActive[nHeight]; return pblockindex->GetBlockHash().GetHex(); } UniValue getblockheader(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getblockheader \"hash\" ( verbose )\n" "\nIf verbose is false, returns a string that is serialized, " "hex-encoded data for blockheader 'hash'.\n" "If verbose is true, returns an Object with information about " "blockheader .\n" "\nArguments:\n" "1. \"hash\" (string, required) The block hash\n" "2. verbose (boolean, optional, default=true) true for a " "json object, false for the hex encoded data\n" "\nResult (for verbose = true):\n" "{\n" " \"hash\" : \"hash\", (string) the block hash (same as " "provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, " "or -1 if the block is not on the main chain\n" " \"height\" : n, (numeric) The block height or index\n" " \"version\" : n, (numeric) The block version\n" " \"versionHex\" : \"00000000\", (string) The block version " "formatted in hexadecimal\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" " \"time\" : ttt, (numeric) The block time in seconds " "since epoch (Jan 1 1970 GMT)\n" " \"mediantime\" : ttt, (numeric) The median block time in " "seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"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" " \"previousblockhash\" : \"hash\", (string) The hash of the " "previous block\n" " \"nextblockhash\" : \"hash\", (string) The hash of the " "next block\n" "}\n" "\nResult (for verbose=false):\n" "\"data\" (string) A string that is serialized, " "hex-encoded data for block 'hash'.\n" "\nExamples:\n" + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec3" "7b049d214adbda81d7e2a3dd146f6ed09" "\"") + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec3" "7b049d214adbda81d7e2a3dd146f6ed09" "\"")); } LOCK(cs_main); std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); bool fVerbose = true; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { fVerbose = request.params[1].get_bool(); } if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; if (!fVerbose) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); ssBlock << pblockindex->GetBlockHeader(); std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); return strHex; } return blockheaderToJSON(pblockindex); } UniValue getblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getblock \"blockhash\" ( verbosity )\n" "\nIf verbosity is 0 or false, returns a string that is " "serialized, hex-encoded data for block 'hash'.\n" "If verbosity is 1 or true, returns an Object with information " "about block .\n" "If verbosity is 2, returns an Object with information about block " " and information about each transaction.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) The block hash\n" "2. verbosity (numeric, optional, default=1) 0 for " "hex-encoded data, 1 for a json object, and 2 for json object with " "transaction data\n" "\nResult (for verbosity = 0):\n" "\"data\" (string) A string that is serialized, " "hex-encoded data for block 'hash'.\n" "\nResult (for verbosity = 1):\n" "{\n" " \"hash\" : \"hash\", (string) The block hash (same as " "provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, " "or -1 if the block is not on the main chain\n" " \"size\" : n, (numeric) The block size\n" " \"height\" : n, (numeric) The block height or index\n" " \"version\" : n, (numeric) The block version\n" " \"versionHex\" : \"00000000\", (string) The block version " "formatted in hexadecimal\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" " \"tx\" : [ (array of string) The transaction ids\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" " ],\n" " \"time\" : ttt, (numeric) The block time in seconds " "since epoch (Jan 1 1970 GMT)\n" " \"mediantime\" : ttt, (numeric) The median block time in " "seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"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" " \"previousblockhash\" : \"hash\", (string) The hash of the " "previous block\n" " \"nextblockhash\" : \"hash\" (string) The hash of the " "next block\n" "}\n" "\nResult (for verbosity = 2):\n" "{\n" " ..., Same output as verbosity = 1\n" " \"tx\" : [ (array of Objects) The transactions in " "the format of the getrawtransaction RPC; different from verbosity " "= 1 \"tx\" result\n" " ...\n" " ],\n" " ... Same output as verbosity = 1\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d" "214adbda81d7e2a3dd146f6ed09\"") + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d" "214adbda81d7e2a3dd146f6ed09\"")); } LOCK(cs_main); std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); int verbosity = 1; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { if (request.params[1].isNum()) { verbosity = request.params[1].get_int(); } else { verbosity = request.params[1].get_bool() ? 1 : 0; } } if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlock block; CBlockIndex *pblockindex = mapBlockIndex[hash]; if (fHavePruned && !pblockindex->nStatus.hasData() && pblockindex->nTx > 0) { throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); } if (!ReadBlockFromDisk(block, pblockindex, config)) { // 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"); } if (verbosity <= 0) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); ssBlock << block; std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); return strHex; } return blockToJSON(config, block, pblockindex, verbosity >= 2); } struct CCoinsStats { int nHeight; uint256 hashBlock; uint64_t nTransactions; uint64_t nTransactionOutputs; uint64_t nBogoSize; uint256 hashSerialized; uint64_t nDiskSize; Amount nTotalAmount; CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount() {} }; static void ApplyStats(CCoinsStats &stats, CHashWriter &ss, const uint256 &hash, const std::map &outputs) { assert(!outputs.empty()); ss << hash; ss << VARINT(outputs.begin()->second.GetHeight() * 2 + outputs.begin()->second.IsCoinBase()); stats.nTransactions++; for (const auto output : outputs) { ss << VARINT(output.first + 1); ss << output.second.GetTxOut().scriptPubKey; ss << VARINT(output.second.GetTxOut().nValue / SATOSHI); stats.nTransactionOutputs++; stats.nTotalAmount += output.second.GetTxOut().nValue; stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ + 2 /* scriptPubKey len */ + output.second.GetTxOut().scriptPubKey.size() /* scriptPubKey */; } ss << VARINT(0); } //! Calculate statistics about the unspent transaction output set static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) { std::unique_ptr pcursor(view->Cursor()); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = pcursor->GetBestBlock(); { LOCK(cs_main); stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight; } ss << stats.hashBlock; uint256 prevkey; std::map outputs; while (pcursor->Valid()) { boost::this_thread::interruption_point(); COutPoint key; Coin coin; if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { if (!outputs.empty() && key.GetTxId() != prevkey) { ApplyStats(stats, ss, prevkey, outputs); outputs.clear(); } prevkey = key.GetTxId(); outputs[key.GetN()] = std::move(coin); } else { return error("%s: unable to read value", __func__); } pcursor->Next(); } if (!outputs.empty()) { ApplyStats(stats, ss, prevkey, outputs); } stats.hashSerialized = ss.GetHash(); stats.nDiskSize = view->EstimateSize(); return true; } UniValue pruneblockchain(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "pruneblockchain\n" "\nArguments:\n" "1. \"height\" (numeric, required) The block height to prune " "up to. May be set to a discrete height, or a unix timestamp\n" " to prune blocks whose block time is at least 2 " "hours older than the provided timestamp.\n" "\nResult:\n" "n (numeric) Height of the last block pruned.\n" "\nExamples:\n" + HelpExampleCli("pruneblockchain", "1000") + HelpExampleRpc("pruneblockchain", "1000")); } if (!fPruneMode) { throw JSONRPCError( RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode."); } LOCK(cs_main); int heightParam = request.params[0].get_int(); if (heightParam < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); } // Height value more than a billion is too high to be a block height, and // too low to be a block time (corresponds to timestamp from Sep 2001). if (heightParam > 1000000000) { // Add a 2 hour buffer to include blocks which might have had old // timestamps CBlockIndex *pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW); if (!pindex) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp."); } heightParam = pindex->nHeight; } unsigned int height = (unsigned int)heightParam; unsigned int chainHeight = (unsigned int)chainActive.Height(); if (chainHeight < config.GetChainParams().PruneAfterHeight()) { throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning."); } 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."); height = chainHeight - MIN_BLOCKS_TO_KEEP; } PruneBlockFilesManual(height); return uint64_t(height); } UniValue gettxoutsetinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "gettxoutsetinfo\n" "\nReturns statistics about the unspent transaction output set.\n" "Note this call may take some time.\n" "\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" " \"bogosize\": n, (numeric) A database-independent " "metric for UTXO set size\n" " \"hash_serialized\": \"hash\", (string) The serialized hash\n" " \"disk_size\": n, (numeric) The estimated size of the " "chainstate on disk\n" " \"total_amount\": x.xxx (numeric) The total amount\n" "}\n" "\nExamples:\n" + HelpExampleCli("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", "")); } UniValue ret(UniValue::VOBJ); CCoinsStats stats; FlushStateToDisk(); if (GetUTXOStats(pcoinsdbview.get(), stats)) { ret.pushKV("height", int64_t(stats.nHeight)); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("transactions", int64_t(stats.nTransactions)); ret.pushKV("txouts", int64_t(stats.nTransactionOutputs)); ret.pushKV("bogosize", int64_t(stats.nBogoSize)); ret.pushKV("hash_serialized", stats.hashSerialized.GetHex()); ret.pushKV("disk_size", stats.nDiskSize); ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount)); } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } return ret; } UniValue gettxout(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { throw std::runtime_error( "gettxout \"txid\" n ( include_mempool )\n" "\nReturns details about an unspent transaction output.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "2. \"n\" (numeric, required) vout number\n" "3. \"include_mempool\" (boolean, optional) Whether to include " "the mempool. Default: true." " 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" " \"confirmations\" : n, (numeric) The number of " "confirmations\n" " \"value\" : x.xxx, (numeric) The transaction value " "in " + CURRENCY_UNIT + "\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"code\", (string) \n" " \"hex\" : \"hex\", (string) \n" " \"reqSigs\" : n, (numeric) Number of required " "signatures\n" " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" " \"addresses\" : [ (array of string) array of " "bitcoin addresses\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ]\n" " },\n" " \"coinbase\" : true|false (boolean) Coinbase or not\n" "}\n" "\nExamples:\n" "\nGet unspent transactions\n" + HelpExampleCli("listunspent", "") + "\nView the details\n" + HelpExampleCli("gettxout", "\"txid\" 1") + "\nAs a json rpc call\n" + HelpExampleRpc("gettxout", "\"txid\", 1")); } LOCK(cs_main); UniValue ret(UniValue::VOBJ); std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); int n = request.params[1].get_int(); COutPoint out(hash, n); bool fMempool = true; - if (request.params.size() > 2) { + if (!request.params[2].isNull()) { fMempool = request.params[2].get_bool(); } Coin coin; if (fMempool) { LOCK(g_mempool.cs); CCoinsViewMemPool view(pcoinsTip.get(), g_mempool); if (!view.GetCoin(out, coin) || g_mempool.isSpent(out)) { // TODO: this should be done by the CCoinsViewMemPool return NullUniValue; } } else { if (!pcoinsTip->GetCoin(out, coin)) { return NullUniValue; } } BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); CBlockIndex *pindex = it->second; ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.GetHeight() == MEMPOOL_HEIGHT) { ret.pushKV("confirmations", 0); } else { ret.pushKV("confirmations", int64_t(pindex->nHeight - coin.GetHeight() + 1)); } ret.pushKV("value", ValueFromAmount(coin.GetTxOut().nValue)); UniValue o(UniValue::VOBJ); ScriptPubKeyToUniv(coin.GetTxOut().scriptPubKey, o, true); ret.pushKV("scriptPubKey", o); ret.pushKV("coinbase", coin.IsCoinBase()); return ret; } UniValue verifychain(const Config &config, const JSONRPCRequest &request) { int nCheckLevel = gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL); int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "verifychain ( checklevel nblocks )\n" "\nVerifies blockchain database.\n" "\nArguments:\n" "1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n" "2. nblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n" "\nResult:\n" "true|false (boolean) Verified or not\n" "\nExamples:\n" + HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "")); } LOCK(cs_main); - if (request.params.size() > 0) { + if (!request.params[0].isNull()) { nCheckLevel = request.params[0].get_int(); } - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { nCheckDepth = request.params[1].get_int(); } return CVerifyDB().VerifyDB(config, pcoinsTip.get(), nCheckLevel, nCheckDepth); } /** Implementation of IsSuperMajority with better feedback */ static UniValue SoftForkMajorityDesc(int version, CBlockIndex *pindex, const Consensus::Params &consensusParams) { UniValue rv(UniValue::VOBJ); bool activated = false; switch (version) { case 2: activated = pindex->nHeight >= consensusParams.BIP34Height; break; case 3: activated = pindex->nHeight >= consensusParams.BIP66Height; break; case 4: activated = pindex->nHeight >= consensusParams.BIP65Height; break; case 5: activated = pindex->nHeight >= consensusParams.CSVHeight; break; } rv.pushKV("status", activated); return rv; } static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex *pindex, const Consensus::Params &consensusParams) { UniValue rv(UniValue::VOBJ); rv.pushKV("id", name); rv.pushKV("version", version); rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams)); return rv; } UniValue getblockchaininfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getblockchaininfo\n" "Returns an object containing various state info regarding " "blockchain processing.\n" "\nResult:\n" "{\n" " \"chain\": \"xxxx\", (string) current network name as " "defined in BIP70 (main, test, regtest)\n" " \"blocks\": xxxxxx, (numeric) the current number of " "blocks processed in the server\n" " \"headers\": xxxxxx, (numeric) the current number of " "headers we have validated\n" " \"bestblockhash\": \"...\", (string) the hash of the currently " "best block\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" " \"mediantime\": xxxxxx, (numeric) median time for the " "current best block\n" " \"verificationprogress\": xxxx, (numeric) estimate of " "verification progress [0..1]\n" " \"chainwork\": \"xxxx\" (string) total amount of work in " "active chain, in hexadecimal\n" " \"pruned\": xx, (boolean) if the blocks are subject " "to pruning\n" " \"pruneheight\": xxxxxx, (numeric) lowest-height complete " "block stored\n" " \"softforks\": [ (array) status of softforks in " "progress\n" " {\n" " \"id\": \"xxxx\", (string) name of softfork\n" " \"version\": xx, (numeric) block version\n" " \"reject\": { (object) progress toward " "rejecting pre-softfork blocks\n" " \"status\": xx, (boolean) true if threshold " "reached\n" " },\n" " }, ...\n" " ]\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "")); } LOCK(cs_main); UniValue obj(UniValue::VOBJ); obj.pushKV("chain", config.GetChainParams().NetworkIDString()); 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(chainActive.Tip()))); obj.pushKV("mediantime", int64_t(chainActive.Tip()->GetMedianTimePast())); obj.pushKV("verificationprogress", GuessVerificationProgress(config.GetChainParams().TxData(), chainActive.Tip())); obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex()); obj.pushKV("pruned", fPruneMode); const Consensus::Params &consensusParams = config.GetChainParams().GetConsensus(); CBlockIndex *tip = chainActive.Tip(); UniValue softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); softforks.push_back(SoftForkDesc("csv", 5, tip, consensusParams)); obj.pushKV("softforks", softforks); if (fPruneMode) { CBlockIndex *block = chainActive.Tip(); while (block && block->pprev && block->pprev->nStatus.hasData()) { block = block->pprev; } obj.pushKV("pruneheight", block->nHeight); } return obj; } /** Comparison function for sorting the getchaintips heads. */ struct CompareBlocksByHeight { bool operator()(const CBlockIndex *a, const CBlockIndex *b) const { // Make sure that unequal blocks with the same height do not compare // equal. Use the pointers themselves to make a distinction. if (a->nHeight != b->nHeight) { return (a->nHeight > b->nHeight); } return a < b; } }; UniValue getchaintips(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getchaintips\n" "Return information about all known tips in the block tree," " including the main chain as well as orphaned branches.\n" "\nResult:\n" "[\n" " {\n" " \"height\": xxxx, (numeric) height of the chain tip\n" " \"hash\": \"xxxx\", (string) block hash of the tip\n" " \"branchlen\": 0 (numeric) zero for main chain\n" " \"status\": \"active\" (string) \"active\" for the main " "chain\n" " },\n" " {\n" " \"height\": xxxx,\n" " \"hash\": \"xxxx\",\n" " \"branchlen\": 1 (numeric) length of branch " "connecting the tip to the main chain\n" " \"status\": \"xxxx\" (string) status of the chain " "(active, valid-fork, valid-headers, headers-only, invalid)\n" " }\n" "]\n" "Possible values for status:\n" "1. \"invalid\" This branch contains at least one " "invalid block\n" "2. \"parked\" This branch contains at least one " "parked block\n" "3. \"headers-only\" Not all blocks for this branch are " "available, but the headers are valid\n" "4. \"valid-headers\" All blocks are available for this " "branch, but they were never fully validated\n" "5. \"valid-fork\" This branch is not part of the " "active chain, but is fully validated\n" "6. \"active\" This is the tip of the active main " "chain, which is certainly valid\n" "\nExamples:\n" + HelpExampleCli("getchaintips", "") + HelpExampleRpc("getchaintips", "")); } LOCK(cs_main); /** * Idea: the set of chain tips is chainActive.tip, plus orphan blocks which * do not have another orphan building off of them. * Algorithm: * - Make one pass through mapBlockIndex, picking out the orphan blocks, * and also storing a set of the orphan block's pprev pointers. * - Iterate through the orphan blocks. If the block isn't pointed to by * another orphan, it is a chain tip. * - add chainActive.Tip() */ std::set setTips; std::set setOrphans; std::set setPrevs; for (const std::pair &item : mapBlockIndex) { if (!chainActive.Contains(item.second)) { setOrphans.insert(item.second); setPrevs.insert(item.second->pprev); } } for (std::set::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) { if (setPrevs.erase(*it) == 0) { setTips.insert(*it); } } // Always report the currently active tip. setTips.insert(chainActive.Tip()); /* Construct the output array. */ UniValue res(UniValue::VARR); for (const CBlockIndex *block : setTips) { UniValue obj(UniValue::VOBJ); obj.pushKV("height", block->nHeight); obj.pushKV("hash", block->phashBlock->GetHex()); const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight; obj.pushKV("branchlen", branchLen); std::string status; if (chainActive.Contains(block)) { // This block is part of the currently active chain. status = "active"; } else if (block->nStatus.isInvalid()) { // This block or one of its ancestors is invalid. status = "invalid"; } else if (block->nStatus.isOnParkedChain()) { // This block or one of its ancestors is parked. status = "parked"; } else if (block->nChainTx == 0) { // This block cannot be connected because full block data for it or // one of its parents is missing. status = "headers-only"; } else if (block->IsValid(BlockValidity::SCRIPTS)) { // This block is fully validated, but no longer part of the active // chain. It was probably the active block once, but was // reorganized. status = "valid-fork"; } else if (block->IsValid(BlockValidity::TREE)) { // The headers for this block are valid, but it has not been // validated. It was probably never part of the most-work chain. status = "valid-headers"; } else { // No clue. status = "unknown"; } obj.pushKV("status", status); res.push_back(obj); } return res; } UniValue mempoolInfoToJSON() { UniValue ret(UniValue::VOBJ); ret.pushKV("size", (int64_t)g_mempool.size()); ret.pushKV("bytes", (int64_t)g_mempool.GetTotalTxSize()); ret.pushKV("usage", (int64_t)g_mempool.DynamicMemoryUsage()); size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; ret.pushKV("maxmempool", (int64_t)maxmempool); ret.pushKV("mempoolminfee", ValueFromAmount(g_mempool.GetMinFee(maxmempool).GetFeePerK())); return ret; } UniValue getmempoolinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getmempoolinfo\n" "\nReturns details on the active state of the TX memory pool.\n" "\nResult:\n" "{\n" " \"size\": xxxxx, (numeric) Current tx count\n" " \"bytes\": xxxxx, (numeric) Transaction size.\n" " \"usage\": xxxxx, (numeric) Total memory usage for " "the mempool\n" " \"maxmempool\": xxxxx, (numeric) Maximum memory usage " "for the mempool\n" " \"mempoolminfee\": xxxxx (numeric) Minimum fee for tx to " "be accepted\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolinfo", "") + HelpExampleRpc("getmempoolinfo", "")); } return mempoolInfoToJSON(); } UniValue preciousblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "preciousblock \"blockhash\"\n" "\nTreats a block as if it were received before others with the " "same work.\n" "\nA later preciousblock call can override the effect of an " "earlier one.\n" "\nThe effects of preciousblock are not retained across restarts.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of the block to " "mark as precious\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("preciousblock", "\"blockhash\"") + HelpExampleRpc("preciousblock", "\"blockhash\"")); } std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); CBlockIndex *pblockindex; { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } pblockindex = mapBlockIndex[hash]; } CValidationState state; PreciousBlock(config, state, pblockindex); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } UniValue finalizeblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "finalizeblock \"blockhash\"\n" "\nTreats a block as final. It cannot be reorged. Any chain\n" "that does not contain this block is invalid. Used on a less\n" "work chain, it can effectively PUTS YOU OUT OF CONSENSUS.\n" "USE WITH CAUTION!\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("finalizeblock", "\"blockhash\"") + HelpExampleRpc("finalizeblock", "\"blockhash\"")); } std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); CValidationState state; { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; FinalizeBlockAndInvalidate(config, state, pblockindex); } if (state.IsValid()) { ActivateBestChain(config, state); } if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } UniValue invalidateblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "invalidateblock \"blockhash\"\n" "\nPermanently marks a block as invalid, as if it " "violated a consensus rule.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of " "the block to mark as invalid\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("invalidateblock", "\"blockhash\"") + HelpExampleRpc("invalidateblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); CValidationState state; { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; InvalidateBlock(config, state, pblockindex); } if (state.IsValid()) { ActivateBestChain(config, state); } if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } UniValue parkblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error("parkblock \"blockhash\"\n" "\nMarks a block as parked.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the " "hash of the block to park\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("parkblock", "\"blockhash\"") + HelpExampleRpc("parkblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); CValidationState state; { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; ParkBlock(config, state, pblockindex); } if (state.IsValid()) { ActivateBestChain(config, state); } if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } UniValue reconsiderblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "reconsiderblock \"blockhash\"\n" "\nRemoves invalidity status of a block and its descendants, " "reconsider them for activation.\n" "This can be used to undo the effects of invalidateblock.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of the block to " "reconsider\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("reconsiderblock", "\"blockhash\"") + HelpExampleRpc("reconsiderblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; ResetBlockFailureFlags(pblockindex); } CValidationState state; ActivateBestChain(config, state); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } UniValue unparkblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "unparkblock \"blockhash\"\n" "\nRemoves parked status of a block and its descendants, " "reconsider them for activation.\n" "This can be used to undo the effects of parkblock.\n" "\nArguments:\n" "1. \"blockhash\" (string, required) the hash of the block to " "unpark\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("unparkblock", "\"blockhash\"") + HelpExampleRpc("unparkblock", "\"blockhash\"")); } const std::string strHash = request.params[0].get_str(); const uint256 hash(uint256S(strHash)); { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } CBlockIndex *pblockindex = mapBlockIndex[hash]; UnparkBlockAndChildren(pblockindex); } CValidationState state; ActivateBestChain(config, state); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } return NullUniValue; } UniValue getchaintxstats(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "getchaintxstats ( nblocks blockhash )\n" "\nCompute statistics about the total number and rate of " "transactions in the chain.\n" "\nArguments:\n" "1. nblocks (numeric, optional) Size of the window in number " "of blocks (default: one month).\n" "2. \"blockhash\" (string, optional) The hash of the block that " "ends the window.\n" "\nResult:\n" "{\n" " \"time\": xxxxx, (numeric) The timestamp for the " "final block in the window in UNIX format.\n" " \"txcount\": xxxxx, (numeric) The total number of " "transactions in the chain up to that point.\n" " \"window_block_count\": xxxxx, (numeric) Size of the window in " "number of blocks.\n" " \"window_tx_count\": xxxxx, (numeric) The number of " "transactions in the window. Only returned if " "\"window_block_count\" is > 0.\n" " \"window_interval\": xxxxx, (numeric) The elapsed time in " "the window in seconds. Only returned if \"window_block_count\" is " "> 0.\n" " \"txrate\": x.xx, (numeric) The average rate of " "transactions per second in the window. Only returned if " "\"window_interval\" is > 0.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getchaintxstats", "") + HelpExampleRpc("getchaintxstats", "2016")); } const CBlockIndex *pindex; // By default: 1 month int blockcount = 30 * 24 * 60 * 60 / config.GetChainParams().GetConsensus().nPowTargetSpacing; bool havehash = !request.params[1].isNull(); uint256 hash; if (havehash) { hash = uint256S(request.params[1].get_str()); } { LOCK(cs_main); if (havehash) { auto it = mapBlockIndex.find(hash); if (it == mapBlockIndex.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } pindex = it->second; if (!chainActive.Contains(pindex)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); } } else { pindex = chainActive.Tip(); } } assert(pindex != nullptr); if (request.params[0].isNull()) { blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); } else { blockcount = request.params[0].get_int(); if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: " "should be between 0 and " "the block's height - 1"); } } const CBlockIndex *pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount); int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast(); int nTxDiff = pindex->nChainTx - pindexPast->nChainTx; UniValue ret(UniValue::VOBJ); ret.pushKV("time", int64_t(pindex->nTime)); ret.pushKV("txcount", int64_t(pindex->nChainTx)); ret.pushKV("window_block_count", blockcount); if (blockcount > 0) { ret.pushKV("window_tx_count", nTxDiff); ret.pushKV("window_interval", nTimeDiff); if (nTimeDiff > 0) { ret.pushKV("txrate", double(nTxDiff) / nTimeDiff); } } return ret; } // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- { "blockchain", "getblockchaininfo", getblockchaininfo, {} }, { "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} }, { "blockchain", "getbestblockhash", getbestblockhash, {} }, { "blockchain", "getblockcount", getblockcount, {} }, { "blockchain", "getblock", getblock, {"blockhash","verbosity|verbose"} }, { "blockchain", "getblockhash", getblockhash, {"height"} }, { "blockchain", "getblockheader", getblockheader, {"blockhash","verbose"} }, { "blockchain", "getchaintips", getchaintips, {} }, { "blockchain", "getdifficulty", getdifficulty, {} }, { "blockchain", "getmempoolancestors", getmempoolancestors, {"txid","verbose"} }, { "blockchain", "getmempooldescendants", getmempooldescendants, {"txid","verbose"} }, { "blockchain", "getmempoolentry", getmempoolentry, {"txid"} }, { "blockchain", "getmempoolinfo", getmempoolinfo, {} }, { "blockchain", "getrawmempool", getrawmempool, {"verbose"} }, { "blockchain", "gettxout", gettxout, {"txid","n","include_mempool"} }, { "blockchain", "gettxoutsetinfo", gettxoutsetinfo, {} }, { "blockchain", "pruneblockchain", pruneblockchain, {"height"} }, { "blockchain", "verifychain", verifychain, {"checklevel","nblocks"} }, { "blockchain", "preciousblock", preciousblock, {"blockhash"} }, /* Not shown in help */ { "hidden", "getfinalizedblockhash", getfinalizedblockhash, {} }, { "hidden", "finalizeblock", finalizeblock, {"blockhash"} }, { "hidden", "invalidateblock", invalidateblock, {"blockhash"} }, { "hidden", "parkblock", parkblock, {"blockhash"} }, { "hidden", "reconsiderblock", reconsiderblock, {"blockhash"} }, { "hidden", "unparkblock", unparkblock, {"blockhash"} }, { "hidden", "waitfornewblock", waitfornewblock, {"timeout"} }, { "hidden", "waitforblock", waitforblock, {"blockhash","timeout"} }, { "hidden", "waitforblockheight", waitforblockheight, {"height","timeout"} }, }; // clang-format on void RegisterBlockchainRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 585bd2e87c..358b7d5faa 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1,821 +1,821 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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/mining.h" #include "amount.h" #include "blockvalidity.h" #include "chain.h" #include "chainparams.h" #include "config.h" #include "consensus/consensus.h" #include "consensus/params.h" #include "consensus/validation.h" #include "core_io.h" #include "dstencode.h" #include "init.h" #include "miner.h" #include "net.h" #include "policy/policy.h" #include "pow.h" #include "rpc/blockchain.h" #include "rpc/server.h" #include "txmempool.h" #include "util.h" #include "utilstrencodings.h" #include "validation.h" #include "validationinterface.h" #include "warnings.h" #include #include #include /** * Return average network hashes per second based on the last 'lookup' blocks, * 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. */ static UniValue GetNetworkHashPS(int lookup, int height) { CBlockIndex *pb = chainActive.Tip(); if (height >= 0 && height < chainActive.Height()) { pb = chainActive[height]; } if (pb == nullptr || !pb->nHeight) { return 0; } // If lookup is -1, then use blocks since last difficulty change. if (lookup <= 0) { lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1; } // If lookup is larger than chain, then set it to chain length. if (lookup > pb->nHeight) { lookup = pb->nHeight; } CBlockIndex *pb0 = pb; int64_t minTime = pb0->GetBlockTime(); int64_t maxTime = minTime; for (int i = 0; i < lookup; i++) { pb0 = pb0->pprev; int64_t time = pb0->GetBlockTime(); minTime = std::min(time, minTime); maxTime = std::max(time, maxTime); } // In case there's a situation where minTime == maxTime, we don't want a // divide by zero exception. if (minTime == maxTime) { return 0; } arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork; int64_t timeDiff = maxTime - minTime; return workDiff.getdouble() / timeDiff; } static UniValue getnetworkhashps(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "getnetworkhashps ( nblocks height )\n" "\nReturns the estimated network hashes per second based on the " "last n blocks.\n" "Pass in [blocks] to override # of blocks, -1 specifies since last " "difficulty change.\n" "Pass in [height] to estimate the network speed at the time when a " "certain block was found.\n" "\nArguments:\n" "1. nblocks (numeric, optional, default=120) The number of " "blocks, or -1 for blocks since last difficulty change.\n" "2. height (numeric, optional, default=-1) To estimate at the " "time of the given height.\n" "\nResult:\n" "x (numeric) Hashes per second estimated\n" "\nExamples:\n" + HelpExampleCli("getnetworkhashps", "") + HelpExampleRpc("getnetworkhashps", "")); } LOCK(cs_main); return GetNetworkHashPS( - request.params.size() > 0 ? request.params[0].get_int() : 120, - request.params.size() > 1 ? request.params[1].get_int() : -1); + !request.params[0].isNull() ? request.params[0].get_int() : 120, + !request.params[1].isNull() ? request.params[1].get_int() : -1); } UniValue generateBlocks(const Config &config, std::shared_ptr coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript) { static const int nInnerLoopCount = 0x100000; int nHeightStart = 0; int nHeightEnd = 0; int nHeight = 0; { // Don't keep cs_main locked. LOCK(cs_main); nHeightStart = chainActive.Height(); nHeight = nHeightStart; nHeightEnd = nHeightStart + nGenerate; } unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd) { std::unique_ptr pblocktemplate( BlockAssembler(config, g_mempool) .CreateNewBlock(coinbaseScript->reserveScript)); if (!pblocktemplate.get()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); } CBlock *pblock = &pblocktemplate->block; { LOCK(cs_main); IncrementExtraNonce(config, pblock, chainActive.Tip(), nExtraNonce); } while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, config)) { ++pblock->nNonce; --nMaxTries; } if (nMaxTries == 0) { break; } if (pblock->nNonce == nInnerLoopCount) { continue; } std::shared_ptr shared_pblock = std::make_shared(*pblock); if (!ProcessNewBlock(config, shared_pblock, true, nullptr)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); } ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); // Mark script as important because it was used at least for one // coinbase output if the script came from the wallet. if (keepScript) { coinbaseScript->KeepScript(); } } return blockHashes; } static UniValue generatetoaddress(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { throw std::runtime_error( "generatetoaddress nblocks address (maxtries)\n" "\nMine blocks immediately to a specified address (before the RPC " "call returns)\n" "\nArguments:\n" "1. nblocks (numeric, required) How many blocks are generated " "immediately.\n" "2. address (string, required) The address to send the newly " "generated bitcoin to.\n" "3. maxtries (numeric, optional) How many iterations to try " "(default = 1000000).\n" "\nResult:\n" "[ blockhashes ] (array) hashes of blocks generated\n" "\nExamples:\n" "\nGenerate 11 blocks to myaddress\n" + HelpExampleCli("generatetoaddress", "11 \"myaddress\"")); } int nGenerate = request.params[0].get_int(); uint64_t nMaxTries = 1000000; - if (request.params.size() > 2) { + if (!request.params[2].isNull()) { nMaxTries = request.params[2].get_int(); } CTxDestination destination = DecodeDestination(request.params[1].get_str(), config.GetChainParams()); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); } std::shared_ptr coinbaseScript = std::make_shared(); coinbaseScript->reserveScript = GetScriptForDestination(destination); return generateBlocks(config, coinbaseScript, nGenerate, nMaxTries, false); } static UniValue getmininginfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getmininginfo\n" "\nReturns a json object containing mining-related information." "\nResult:\n" "{\n" " \"blocks\": nnn, (numeric) The current block\n" " \"currentblocksize\": nnn, (numeric) The last block size\n" " \"currentblocktx\": nnn, (numeric) The last block " "transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" " \"errors\": \"...\" (string) Current errors\n" " \"networkhashps\": nnn, (numeric) The network hashes per " "second\n" " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as " "defined in BIP70 (main, test, regtest)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") + HelpExampleRpc("getmininginfo", "")); } LOCK(cs_main); UniValue obj(UniValue::VOBJ); obj.pushKV("blocks", int(chainActive.Height())); obj.pushKV("currentblocksize", uint64_t(nLastBlockSize)); obj.pushKV("currentblocktx", uint64_t(nLastBlockTx)); obj.pushKV("difficulty", double(GetDifficulty(chainActive.Tip()))); obj.pushKV("blockprioritypercentage", uint8_t(gArgs.GetArg("-blockprioritypercentage", DEFAULT_BLOCK_PRIORITY_PERCENTAGE))); obj.pushKV("errors", GetWarnings("statusbar")); obj.pushKV("networkhashps", getnetworkhashps(config, request)); obj.pushKV("pooledtx", uint64_t(g_mempool.size())); obj.pushKV("chain", config.GetChainParams().NetworkIDString()); return obj; } // NOTE: Unlike wallet RPC (which use BCH values), mining RPCs follow GBT (BIP // 22) in using satoshi amounts static UniValue prioritisetransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 3) { throw std::runtime_error( "prioritisetransaction \n" "Accepts the transaction into mined blocks at a higher (or lower) " "priority\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id.\n" "2. priority_delta (numeric, required) The priority to add or " "subtract.\n" " The transaction selection algorithm considers " "the tx as it would have a higher priority.\n" " (priority of a transaction is calculated: " "coinage * value_in_satoshis / txsize) \n" "3. fee_delta (numeric, required) The fee value (in satoshis) " "to add (or subtract, if negative).\n" " The fee is not actually paid, only the " "algorithm for selecting transactions into a block\n" " considers the transaction as it would have paid " "a higher (or lower) fee.\n" "\nResult:\n" "true (boolean) Returns true\n" "\nExamples:\n" + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")); } LOCK(cs_main); uint256 hash = ParseHashStr(request.params[0].get_str(), "txid"); Amount nAmount = request.params[2].get_int64() * SATOSHI; g_mempool.PrioritiseTransaction(hash, request.params[0].get_str(), request.params[1].get_real(), nAmount); return true; } // NOTE: Assumes a conclusive result; if result is inconclusive, it must be // handled by caller static UniValue BIP22ValidationResult(const Config &config, const CValidationState &state) { if (state.IsValid()) { return NullUniValue; } std::string strRejectReason = state.GetRejectReason(); if (state.IsError()) { throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason); } if (state.IsInvalid()) { if (strRejectReason.empty()) { return "rejected"; } return strRejectReason; } // Should be impossible. return "valid?"; } static UniValue getblocktemplate(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getblocktemplate ( TemplateRequest )\n" "\nIf the request parameters include a 'mode' key, that is used to " "explicitly select between the default 'template' request or a " "'proposal'.\n" "It returns data needed to construct a block to work on.\n" "For full specification, see BIPs 22, 23, 9, and 145:\n" " " "https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" " " "https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n" " " "https://github.com/bitcoin/bips/blob/master/" "bip-0009.mediawiki#getblocktemplate_changes\n" " " "https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n" "\nArguments:\n" "1. template_request (json object, optional) A json object " "in the following spec\n" " {\n" " \"mode\":\"template\" (string, optional) This must be " "set to \"template\", \"proposal\" (see BIP 23), or omitted\n" " \"capabilities\":[ (array, optional) A list of " "strings\n" " \"support\" (string) client side supported " "feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', " "'serverlist', 'workid'\n" " ,...\n" " ]\n" " }\n" "\n" "\nResult:\n" "{\n" " \"version\" : n, (numeric) The preferred " "block version\n" " \"previousblockhash\" : \"xxxx\", (string) The hash of " "current highest block\n" " \"transactions\" : [ (array) contents of " "non-coinbase transactions that should be included in the next " "block\n" " {\n" " \"data\" : \"xxxx\", (string) transaction " "data encoded in hexadecimal (byte-for-byte)\n" " \"txid\" : \"xxxx\", (string) transaction id " "encoded in little-endian hexadecimal\n" " \"hash\" : \"xxxx\", (string) hash encoded " "in little-endian hexadecimal (including witness data)\n" " \"depends\" : [ (array) array of numbers " "\n" " n (numeric) transactions " "before this one (by 1-based index in 'transactions' list) that " "must be present in the final block if this one is\n" " ,...\n" " ],\n" " \"fee\": n, (numeric) difference in " "value between transaction inputs and outputs (in Satoshis); for " "coinbase transactions, this is a negative Number of the total " "collected block fees (ie, not including the block subsidy); if " "key is not present, fee is unknown and clients MUST NOT assume " "there isn't one\n" " \"sigops\" : n, (numeric) total 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" " \"required\" : true|false (boolean) if provided and " "true, this transaction must be in the final block\n" " }\n" " ,...\n" " ],\n" " \"coinbaseaux\" : { (json object) data that " "should be included in the coinbase's scriptSig content\n" " \"flags\" : \"xx\" (string) key name is to " "be ignored, and value included in scriptSig\n" " },\n" " \"coinbasevalue\" : n, (numeric) maximum allowable " "input to coinbase transaction, including the generation award and " "transaction fees (in Satoshis)\n" " \"coinbasetxn\" : { ... }, (json object) information " "for coinbase transaction\n" " \"target\" : \"xxxx\", (string) The hash target\n" " \"mintime\" : xxx, (numeric) The minimum " "timestamp appropriate for next block time in seconds since epoch " "(Jan 1 1970 GMT)\n" " \"mutable\" : [ (array of string) list of " "ways the block template may be changed \n" " \"value\" (string) A way the block " "template may be changed, e.g. 'time', 'transactions', " "'prevblock'\n" " ,...\n" " ],\n" " \"noncerange\" : \"00000000ffffffff\",(string) A range of valid " "nonces\n" " \"sigoplimit\" : n, (numeric) limit of sigops " "in blocks\n" " \"sizelimit\" : n, (numeric) limit of block " "size\n" " \"curtime\" : ttt, (numeric) current timestamp " "in seconds since epoch (Jan 1 1970 GMT)\n" " \"bits\" : \"xxxxxxxx\", (string) compressed " "target of next block\n" " \"height\" : n (numeric) The height of the " "next block\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblocktemplate", "") + HelpExampleRpc("getblocktemplate", "")); } LOCK(cs_main); std::string strMode = "template"; UniValue lpval = NullUniValue; std::set setClientRules; - if (request.params.size() > 0) { + if (!request.params[0].isNull()) { const UniValue &oparam = request.params[0].get_obj(); const UniValue &modeval = find_value(oparam, "mode"); if (modeval.isStr()) { strMode = modeval.get_str(); } else if (modeval.isNull()) { /* Do nothing */ } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); } lpval = find_value(oparam, "longpollid"); if (strMode == "proposal") { const UniValue &dataval = find_value(oparam, "data"); if (!dataval.isStr()) { throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); } CBlock block; if (!DecodeHexBlk(block, dataval.get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); } uint256 hash = block.GetHash(); BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex *pindex = mi->second; if (pindex->IsValid(BlockValidity::SCRIPTS)) { return "duplicate"; } if (pindex->nStatus.isInvalid()) { return "duplicate-invalid"; } return "duplicate-inconclusive"; } CBlockIndex *const pindexPrev = chainActive.Tip(); // TestBlockValidity only supports blocks built on the current Tip if (block.hashPrevBlock != pindexPrev->GetBlockHash()) { return "inconclusive-not-best-prevblk"; } CValidationState state; BlockValidationOptions validationOptions = BlockValidationOptions(false, true); TestBlockValidity(config, state, block, pindexPrev, validationOptions); return BIP22ValidationResult(config, state); } } if (strMode != "template") { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) { throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!"); } if (IsInitialBlockDownload()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); } static unsigned int nTransactionsUpdatedLast; if (!lpval.isNull()) { // Wait to respond until either the best block changes, OR a minute has // passed and there are more transactions uint256 hashWatchedChain; std::chrono::steady_clock::time_point checktxtime; unsigned int nTransactionsUpdatedLastLP; if (lpval.isStr()) { // Format: std::string lpstr = lpval.get_str(); hashWatchedChain.SetHex(lpstr.substr(0, 64)); nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); } else { // NOTE: Spec does not specify behaviour for non-string longpollid, // but this makes testing easier hashWatchedChain = chainActive.Tip()->GetBlockHash(); nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; } // Release the wallet and main lock while waiting LEAVE_CRITICAL_SECTION(cs_main); { checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); WAIT_LOCK(g_best_block_mutex, lock); while (g_best_block == hashWatchedChain && IsRPCRunning()) { if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update if (g_mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) { break; } checktxtime += std::chrono::seconds(10); } } } ENTER_CRITICAL_SECTION(cs_main); if (!IsRPCRunning()) { throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); } // TODO: Maybe recheck connections/IBD and (if something wrong) send an // expires-immediately template to stop miners? } // Update block static CBlockIndex *pindexPrev; static int64_t nStart; static std::unique_ptr pblocktemplate; if (pindexPrev != chainActive.Tip() || (g_mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any // failures from here on pindexPrev = nullptr; // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = g_mempool.GetTransactionsUpdated(); CBlockIndex *pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block CScript scriptDummy = CScript() << OP_TRUE; pblocktemplate = BlockAssembler(config, g_mempool).CreateNewBlock(scriptDummy); if (!pblocktemplate) { throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); } // Need to update only after we know CreateNewBlock succeeded pindexPrev = pindexPrevNew; } // pointer for convenience CBlock *pblock = &pblocktemplate->block; // Update nTime UpdateTime(pblock, config, pindexPrev); pblock->nNonce = 0; UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); UniValue transactions(UniValue::VARR); std::map setTxIndex; int i = 0; for (const auto &it : pblock->vtx) { const CTransaction &tx = *it; uint256 txId = tx.GetId(); setTxIndex[txId] = i++; if (tx.IsCoinBase()) { continue; } UniValue entry(UniValue::VOBJ); entry.pushKV("data", EncodeHexTx(tx)); entry.pushKV("txid", txId.GetHex()); entry.pushKV("hash", tx.GetHash().GetHex()); UniValue deps(UniValue::VARR); for (const CTxIn &in : tx.vin) { if (setTxIndex.count(in.prevout.GetTxId())) { deps.push_back(setTxIndex[in.prevout.GetTxId()]); } } entry.pushKV("depends", deps); int index_in_template = i - 1; entry.pushKV("fee", pblocktemplate->entries[index_in_template].fees / SATOSHI); int64_t nTxSigOps = pblocktemplate->entries[index_in_template].sigOpCount; entry.pushKV("sigops", nTxSigOps); transactions.push_back(entry); } UniValue aux(UniValue::VOBJ); aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); UniValue aMutable(UniValue::VARR); aMutable.push_back("time"); aMutable.push_back("transactions"); aMutable.push_back("prevblock"); UniValue result(UniValue::VOBJ); result.pushKV("capabilities", aCaps); result.pushKV("version", pblock->nVersion); result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); result.pushKV("transactions", transactions); result.pushKV("coinbaseaux", aux); result.pushKV("coinbasevalue", int64_t(pblock->vtx[0]->vout[0].nValue / SATOSHI)); result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)); result.pushKV("target", hashTarget.GetHex()); result.pushKV("mintime", int64_t(pindexPrev->GetMedianTimePast()) + 1); result.pushKV("mutable", aMutable); result.pushKV("noncerange", "00000000ffffffff"); // FIXME: Allow for mining block greater than 1M. result.pushKV("sigoplimit", GetMaxBlockSigOpsCount(DEFAULT_MAX_BLOCK_SIZE)); result.pushKV("sizelimit", DEFAULT_MAX_BLOCK_SIZE); result.pushKV("curtime", pblock->GetBlockTime()); result.pushKV("bits", strprintf("%08x", pblock->nBits)); result.pushKV("height", int64_t(pindexPrev->nHeight) + 1); return result; } class submitblock_StateCatcher : public CValidationInterface { public: uint256 hash; bool found; CValidationState state; explicit submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {} protected: void BlockChecked(const CBlock &block, const CValidationState &stateIn) override { if (block.GetHash() != hash) { return; } found = true; state = stateIn; } }; static UniValue submitblock(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "submitblock \"hexdata\" ( \"jsonparametersobject\" )\n" "\nAttempts to submit new block to network.\n" "The 'jsonparametersobject' parameter is currently ignored.\n" "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" "\nArguments\n" "1. \"hexdata\" (string, required) the hex-encoded block " "data to submit\n" "2. \"parameters\" (string, optional) object of optional " "parameters\n" " {\n" " \"workid\" : \"id\" (string, optional) if the server " "provided a workid, it MUST be included with submissions\n" " }\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("submitblock", "\"mydata\"") + HelpExampleRpc("submitblock", "\"mydata\"")); } std::shared_ptr blockptr = std::make_shared(); CBlock &block = *blockptr; if (!DecodeHexBlk(block, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); } if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block does not start with a coinbase"); } uint256 hash = block.GetHash(); bool fBlockPresent = false; { LOCK(cs_main); BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex *pindex = mi->second; if (pindex->IsValid(BlockValidity::SCRIPTS)) { return "duplicate"; } if (pindex->nStatus.isInvalid()) { return "duplicate-invalid"; } // Otherwise, we might only have the header - process the block // before returning fBlockPresent = true; } } submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); bool fAccepted = ProcessNewBlock(config, blockptr, true, nullptr); UnregisterValidationInterface(&sc); if (fBlockPresent) { if (fAccepted && !sc.found) { return "duplicate-inconclusive"; } return "duplicate"; } if (!sc.found) { return "inconclusive"; } return BIP22ValidationResult(config, sc.state); } static UniValue estimatefee(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "estimatefee\n" "\nEstimates the approximate fee per kilobyte needed for a " "transaction\n" "\nResult:\n" "n (numeric) estimated fee-per-kilobyte\n" "\nExample:\n" + HelpExampleCli("estimatefee", "")); } if ((request.params.size() == 1) && !IsDeprecatedRPCEnabled(gArgs, "estimatefee")) { // FIXME: Remove this message in 0.20 throw JSONRPCError( RPC_METHOD_DEPRECATED, "estimatefee with the nblocks argument is no longer supported\n" "Please call estimatefee with no arguments instead.\n" "\nExample:\n" + HelpExampleCli("estimatefee", "")); } return ValueFromAmount(g_mempool.estimateFee().GetFeePerK()); } // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ---------- ------------------------ ---------------------- ---------- {"mining", "getnetworkhashps", getnetworkhashps, {"nblocks", "height"}}, {"mining", "getmininginfo", getmininginfo, {}}, {"mining", "prioritisetransaction", prioritisetransaction, {"txid", "priority_delta", "fee_delta"}}, {"mining", "getblocktemplate", getblocktemplate, {"template_request"}}, {"mining", "submitblock", submitblock, {"hexdata", "parameters"}}, {"generating", "generatetoaddress", generatetoaddress, {"nblocks", "address", "maxtries"}}, {"util", "estimatefee", estimatefee, {"nblocks"}}, }; // clang-format on void RegisterMiningRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) t.appendCommand(commands[vcidx].name, &commands[vcidx]); } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 0ce76b0fc5..1b556883c7 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -1,800 +1,800 @@ // Copyright (c) 2009-2016 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 "chainparams.h" #include "clientversion.h" #include "config.h" #include "net.h" #include "net_processing.h" #include "netbase.h" #include "policy/policy.h" #include "protocol.h" #include "sync.h" #include "timedata.h" #include "ui_interface.h" #include "util.h" #include "utilstrencodings.h" #include "validation.h" #include "version.h" #include "warnings.h" #include static UniValue getconnectioncount(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getconnectioncount\n" "\nReturns the number of connections to other nodes.\n" "\nResult:\n" "n (numeric) The connection count\n" "\nExamples:\n" + HelpExampleCli("getconnectioncount", "") + HelpExampleRpc("getconnectioncount", "")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } return int(g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)); } static UniValue ping(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "ping\n" "\nRequests that a ping be sent to all other nodes, to measure " "ping time.\n" "Results provided in getpeerinfo, pingtime and pingwait fields are " "decimal seconds.\n" "Ping command is handled in queue with all other commands, so it " "measures processing backlog, not just network ping.\n" "\nExamples:\n" + HelpExampleCli("ping", "") + HelpExampleRpc("ping", "")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } // Request that each node send a ping during next message processing pass g_connman->ForEachNode([](CNode *pnode) { pnode->fPingQueued = true; }); return NullUniValue; } static UniValue getpeerinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getpeerinfo\n" "\nReturns data about each connected network node as a json array " "of objects.\n" "\nResult:\n" "[\n" " {\n" " \"id\": n, (numeric) Peer index\n" " \"addr\":\"host:port\", (string) The ip address and port " "of the peer\n" " \"addrbind\":\"ip:port\", (string) Bind address of the " "connection to the peer\n" " \"addrlocal\":\"ip:port\", (string) Local address as " "reported by the peer\n" " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services " "offered\n" " \"relaytxes\":true|false, (boolean) Whether peer has asked " "us to relay transactions to it\n" " \"lastsend\": ttt, (numeric) The time in seconds " "since epoch (Jan 1 1970 GMT) of the last send\n" " \"lastrecv\": ttt, (numeric) The time in seconds " "since epoch (Jan 1 1970 GMT) of the last receive\n" " \"bytessent\": n, (numeric) The total bytes sent\n" " \"bytesrecv\": n, (numeric) The total bytes " "received\n" " \"conntime\": ttt, (numeric) The connection time in " "seconds since epoch (Jan 1 1970 GMT)\n" " \"timeoffset\": ttt, (numeric) The time offset in " "seconds\n" " \"pingtime\": n, (numeric) ping time (if " "available)\n" " \"minping\": n, (numeric) minimum observed ping " "time (if any at all)\n" " \"pingwait\": n, (numeric) ping wait (if " "non-zero)\n" " \"version\": v, (numeric) The peer version, such " "as 7001\n" " \"subver\": \"/Satoshi:0.8.5/\", (string) The string " "version\n" " \"inbound\": true|false, (boolean) Inbound (true) or " "Outbound (false)\n" " \"addnode\": true|false, (boolean) Whether connection was " "due to addnode/-connect or if it was an automatic/inbound " "connection\n" " \"startingheight\": n, (numeric) The starting height " "(block) of the peer\n" " \"banscore\": n, (numeric) The ban score\n" " \"synced_headers\": n, (numeric) The last header we " "have in common with this peer\n" " \"synced_blocks\": n, (numeric) The last block we have " "in common with this peer\n" " \"inflight\": [\n" " n, (numeric) The heights of blocks " "we're currently asking from this peer\n" " ...\n" " ],\n" " \"whitelisted\": true|false, (boolean) Whether the peer is " "whitelisted\n" " \"bytessent_per_msg\": {\n" " \"addr\": n, (numeric) The total bytes sent " "aggregated by message type\n" " ...\n" " },\n" " \"bytesrecv_per_msg\": {\n" " \"addr\": n, (numeric) The total bytes " "received aggregated by message type\n" " ...\n" " }\n" " }\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("getpeerinfo", "") + HelpExampleRpc("getpeerinfo", "")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } std::vector vstats; g_connman->GetNodeStats(vstats); UniValue ret(UniValue::VARR); for (const CNodeStats &stats : vstats) { UniValue obj(UniValue::VOBJ); CNodeStateStats statestats; bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); obj.pushKV("id", stats.nodeid); obj.pushKV("addr", stats.addrName); if (!(stats.addrLocal.empty())) { obj.pushKV("addrlocal", stats.addrLocal); } if (stats.addrBind.IsValid()) { obj.pushKV("addrbind", stats.addrBind.ToString()); } obj.pushKV("services", strprintf("%016x", stats.nServices)); obj.pushKV("relaytxes", stats.fRelayTxes); obj.pushKV("lastsend", stats.nLastSend); obj.pushKV("lastrecv", stats.nLastRecv); obj.pushKV("bytessent", stats.nSendBytes); obj.pushKV("bytesrecv", stats.nRecvBytes); obj.pushKV("conntime", stats.nTimeConnected); obj.pushKV("timeoffset", stats.nTimeOffset); if (stats.dPingTime > 0.0) { obj.pushKV("pingtime", stats.dPingTime); } if (stats.dMinPing < std::numeric_limits::max() / 1e6) { obj.pushKV("minping", stats.dMinPing); } if (stats.dPingWait > 0.0) { obj.pushKV("pingwait", stats.dPingWait); } obj.pushKV("version", stats.nVersion); // Use the sanitized form of subver here, to avoid tricksy remote peers // from corrupting or modifying the JSON output by putting special // characters in their ver message. obj.pushKV("subver", stats.cleanSubVer); obj.pushKV("inbound", stats.fInbound); obj.pushKV("addnode", stats.m_manual_connection); obj.pushKV("startingheight", stats.nStartingHeight); if (fStateStats) { obj.pushKV("banscore", statestats.nMisbehavior); obj.pushKV("synced_headers", statestats.nSyncHeight); obj.pushKV("synced_blocks", statestats.nCommonHeight); UniValue heights(UniValue::VARR); for (int height : statestats.vHeightInFlight) { heights.push_back(height); } obj.pushKV("inflight", heights); } obj.pushKV("whitelisted", stats.fWhitelisted); UniValue sendPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapSendBytesPerMsgCmd) { if (i.second > 0) { sendPerMsgCmd.pushKV(i.first, i.second); } } obj.pushKV("bytessent_per_msg", sendPerMsgCmd); UniValue recvPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapRecvBytesPerMsgCmd) { if (i.second > 0) { recvPerMsgCmd.pushKV(i.first, i.second); } } obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd); ret.push_back(obj); } return ret; } static UniValue addnode(const Config &config, const JSONRPCRequest &request) { std::string strCommand; if (request.params.size() == 2) { strCommand = request.params[1].get_str(); } if (request.fHelp || request.params.size() != 2 || (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) { throw std::runtime_error( "addnode \"node\" \"add|remove|onetry\"\n" "\nAttempts add or remove a node from the addnode list.\n" "Or try a connection to a node once.\n" "Nodes added using addnode (or -connect) are protected from DoS " "disconnection and are not required to be\n" "full nodes as other outbound peers are (though " "such peers will not be synced from).\n" "\nArguments:\n" "1. \"node\" (string, required) The node (see getpeerinfo for " "nodes)\n" "2. \"command\" (string, required) 'add' to add a node to the " "list, 'remove' to remove a node from the list, 'onetry' to try a " "connection to the node once\n" "\nExamples:\n" + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } std::string strNode = request.params[0].get_str(); if (strCommand == "onetry") { CAddress addr; g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true); return NullUniValue; } if ((strCommand == "add") && (!g_connman->AddNode(strNode))) { throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); } else if ((strCommand == "remove") && (!g_connman->RemoveAddedNode(strNode))) { throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); } return NullUniValue; } static UniValue disconnectnode(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3) { throw std::runtime_error( "disconnectnode \"[address]\" [nodeid]\n" "\nImmediately disconnects from the specified peer node.\n" "\nStrictly one out of 'address' and 'nodeid' can be provided to " "identify the node.\n" "\nTo disconnect by nodeid, either set 'address' to the empty " "string, or call using the named 'nodeid' argument only.\n" "\nArguments:\n" "1. \"address\" (string, optional) The IP address/port of the " "node\n" "2. \"nodeid\" (number, optional) The node ID (see " "getpeerinfo for node IDs)\n" "\nExamples:\n" + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + HelpExampleCli("disconnectnode", "\"\" 1") + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") + HelpExampleRpc("disconnectnode", "\"\", 1")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } bool success; const UniValue &address_arg = request.params[0]; const UniValue &id_arg = request.params.size() < 2 ? NullUniValue : request.params[1]; if (!address_arg.isNull() && id_arg.isNull()) { /* handle disconnect-by-address */ success = g_connman->DisconnectNode(address_arg.get_str()); } else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) { /* handle disconnect-by-id */ NodeId nodeid = (NodeId)id_arg.get_int64(); success = g_connman->DisconnectNode(nodeid); } else { throw JSONRPCError( RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided."); } if (!success) { throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); } return NullUniValue; } static UniValue getaddednodeinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getaddednodeinfo ( \"node\" )\n" "\nReturns information about the given added node, or all added " "nodes\n" "(note that onetry addnodes are not listed here)\n" "\nArguments:\n" "1. \"node\" (string, optional) If provided, return information " "about this specific node, otherwise all nodes are returned.\n" "\nResult:\n" "[\n" " {\n" " \"addednode\" : \"192.168.0.201\", (string) The node ip " "address or name (as provided to addnode)\n" " \"connected\" : true|false, (boolean) If connected\n" " \"addresses\" : [ (list of objects) Only " "when connected = true\n" " {\n" " \"address\" : \"192.168.0.201:8333\", (string) The " "bitcoin server IP and port we're connected to\n" " \"connected\" : \"outbound\" (string) " "connection, inbound or outbound\n" " }\n" " ]\n" " }\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") + HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } std::vector vInfo = g_connman->GetAddedNodeInfo(); - if (request.params.size() == 1) { + if (request.params.size() == 1 && !request.params[0].isNull()) { bool found = false; for (const AddedNodeInfo &info : vInfo) { if (info.strAddedNode == request.params[0].get_str()) { vInfo.assign(1, info); found = true; break; } } if (!found) { throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); } } UniValue ret(UniValue::VARR); for (const AddedNodeInfo &info : vInfo) { UniValue obj(UniValue::VOBJ); obj.pushKV("addednode", info.strAddedNode); obj.pushKV("connected", info.fConnected); UniValue addresses(UniValue::VARR); if (info.fConnected) { UniValue address(UniValue::VOBJ); address.pushKV("address", info.resolvedAddress.ToString()); address.pushKV("connected", info.fInbound ? "inbound" : "outbound"); addresses.push_back(address); } obj.pushKV("addresses", addresses); ret.push_back(obj); } return ret; } static UniValue getnettotals(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 0) { throw std::runtime_error( "getnettotals\n" "\nReturns information about network traffic, including bytes in, " "bytes out,\n" "and current time.\n" "\nResult:\n" "{\n" " \"totalbytesrecv\": n, (numeric) Total bytes received\n" " \"totalbytessent\": n, (numeric) Total bytes sent\n" " \"timemillis\": t, (numeric) Current UNIX time in " "milliseconds\n" " \"uploadtarget\":\n" " {\n" " \"timeframe\": n, (numeric) Length of " "the measuring timeframe in seconds\n" " \"target\": n, (numeric) Target in " "bytes\n" " \"target_reached\": true|false, (boolean) True if " "target is reached\n" " \"serve_historical_blocks\": true|false, (boolean) True if " "serving historical blocks\n" " \"bytes_left_in_cycle\": t, (numeric) Bytes " "left in current time cycle\n" " \"time_left_in_cycle\": t (numeric) Seconds " "left in current time cycle\n" " }\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnettotals", "") + HelpExampleRpc("getnettotals", "")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } UniValue obj(UniValue::VOBJ); obj.pushKV("totalbytesrecv", g_connman->GetTotalBytesRecv()); obj.pushKV("totalbytessent", g_connman->GetTotalBytesSent()); obj.pushKV("timemillis", GetTimeMillis()); UniValue outboundLimit(UniValue::VOBJ); outboundLimit.pushKV("timeframe", g_connman->GetMaxOutboundTimeframe()); outboundLimit.pushKV("target", g_connman->GetMaxOutboundTarget()); outboundLimit.pushKV("target_reached", g_connman->OutboundTargetReached(false)); outboundLimit.pushKV("serve_historical_blocks", !g_connman->OutboundTargetReached(true)); outboundLimit.pushKV("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft()); outboundLimit.pushKV("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle()); obj.pushKV("uploadtarget", outboundLimit); return obj; } static UniValue GetNetworksInfo() { UniValue networks(UniValue::VARR); for (int n = 0; n < NET_MAX; ++n) { enum Network network = static_cast(n); if (network == NET_UNROUTABLE || network == NET_INTERNAL) { continue; } proxyType proxy; UniValue obj(UniValue::VOBJ); GetProxy(network, proxy); obj.pushKV("name", GetNetworkName(network)); obj.pushKV("limited", IsLimited(network)); obj.pushKV("reachable", IsReachable(network)); obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string()); obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials); networks.push_back(obj); } return networks; } static UniValue getnetworkinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "getnetworkinfo\n" "Returns an object containing various state info regarding P2P " "networking.\n" "\nResult:\n" "{\n" " \"version\": xxxxx, (numeric) the server " "version\n" " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server " "subversion string\n" " \"protocolversion\": xxxxx, (numeric) the protocol " "version\n" " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services " "we offer to the network\n" " \"localrelay\": true|false, (bool) true if " "transaction relay is requested from peers\n" " \"timeoffset\": xxxxx, (numeric) the time " "offset\n" " \"connections\": xxxxx, (numeric) the number " "of connections\n" " \"networkactive\": true|false, (bool) whether p2p " "networking is enabled\n" " \"networks\": [ (array) information " "per network\n" " {\n" " \"name\": \"xxx\", (string) network " "(ipv4, ipv6 or onion)\n" " \"limited\": true|false, (boolean) is the " "network limited using -onlynet?\n" " \"reachable\": true|false, (boolean) is the " "network reachable?\n" " \"proxy\": \"host:port\" (string) the proxy " "that is used for this network, or empty if none\n" " \"proxy_randomize_credentials\": true|false, (string) " "Whether randomized credentials are used\n" " }\n" " ,...\n" " ],\n" " \"relayfee\": x.xxxxxxxx, (numeric) minimum " "relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" " \"excessutxocharge\": x.xxxxxxxx, (numeric) minimum " "charge for excess utxos in " + CURRENCY_UNIT + "\n" " \"localaddresses\": [ " "(array) list of local addresses\n" " {\n" " \"address\": \"xxxx\", " "(string) network address\n" " \"port\": xxx, " "(numeric) network port\n" " \"score\": xxx " "(numeric) relative score\n" " }\n" " ,...\n" " ]\n" " \"warnings\": \"...\" " "(string) any network warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") + HelpExampleRpc("getnetworkinfo", "")); } LOCK(cs_main); UniValue obj(UniValue::VOBJ); obj.pushKV("version", CLIENT_VERSION); obj.pushKV("subversion", userAgent(config)); obj.pushKV("protocolversion", PROTOCOL_VERSION); if (g_connman) { obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices())); } obj.pushKV("localrelay", fRelayTxes); obj.pushKV("timeoffset", GetTimeOffset()); if (g_connman) { obj.pushKV("networkactive", g_connman->GetNetworkActive()); obj.pushKV("connections", int(g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); } obj.pushKV("networks", GetNetworksInfo()); obj.pushKV("relayfee", ValueFromAmount(config.GetMinFeePerKB().GetFeePerK())); obj.pushKV("excessutxocharge", ValueFromAmount(config.GetExcessUTXOCharge())); UniValue localAddresses(UniValue::VARR); { LOCK(cs_mapLocalHost); for (const std::pair &item : mapLocalHost) { UniValue rec(UniValue::VOBJ); rec.pushKV("address", item.first.ToString()); rec.pushKV("port", item.second.nPort); rec.pushKV("score", item.second.nScore); localAddresses.push_back(rec); } } obj.pushKV("localaddresses", localAddresses); obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } static UniValue setban(const Config &config, const JSONRPCRequest &request) { std::string strCommand; if (request.params.size() >= 2) { strCommand = request.params[1].get_str(); } if (request.fHelp || request.params.size() < 2 || (strCommand != "add" && strCommand != "remove")) { throw std::runtime_error( "setban \"subnet\" \"add|remove\" (bantime) (absolute)\n" "\nAttempts add or remove a IP/Subnet from the banned list.\n" "\nArguments:\n" "1. \"subnet\" (string, required) The IP/Subnet (see " "getpeerinfo for nodes ip) with a optional netmask (default is /32 " "= single ip)\n" "2. \"command\" (string, required) 'add' to add a IP/Subnet " "to the list, 'remove' to remove a IP/Subnet from the list\n" "3. \"bantime\" (numeric, optional) time in seconds how long " "(or until when if [absolute] is set) the ip is banned (0 or empty " "means using the default time of 24h which can also be overwritten " "by the -bantime startup argument)\n" "4. \"absolute\" (boolean, optional) If set, the bantime must " "be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" "\nExamples:\n" + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } CSubNet subNet; CNetAddr netAddr; bool isSubnet = false; if (request.params[0].get_str().find("/") != std::string::npos) { isSubnet = true; } if (!isSubnet) { CNetAddr resolved; LookupHost(request.params[0].get_str().c_str(), resolved, false); netAddr = resolved; } else { LookupSubNet(request.params[0].get_str().c_str(), subNet); } if (!(isSubnet ? subNet.IsValid() : netAddr.IsValid())) { throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Invalid IP/Subnet"); } if (strCommand == "add") { if (isSubnet ? g_connman->IsBanned(subNet) : g_connman->IsBanned(netAddr)) { throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); } // Use standard bantime if not specified. int64_t banTime = 0; if (request.params.size() >= 3 && !request.params[2].isNull()) { banTime = request.params[2].get_int64(); } bool absolute = false; if (request.params.size() == 4 && request.params[3].isTrue()) { absolute = true; } isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); } else if (strCommand == "remove") { if (!(isSubnet ? g_connman->Unban(subNet) : g_connman->Unban(netAddr))) { throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet " "was not previously banned."); } } return NullUniValue; } static UniValue listbanned(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error("listbanned\n" "\nList all banned IPs/Subnets.\n" "\nExamples:\n" + HelpExampleCli("listbanned", "") + HelpExampleRpc("listbanned", "")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } banmap_t banMap; g_connman->GetBanned(banMap); UniValue bannedAddresses(UniValue::VARR); for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) { CBanEntry banEntry = (*it).second; UniValue rec(UniValue::VOBJ); rec.pushKV("address", (*it).first.ToString()); rec.pushKV("banned_until", banEntry.nBanUntil); rec.pushKV("ban_created", banEntry.nCreateTime); rec.pushKV("ban_reason", banEntry.banReasonToString()); bannedAddresses.push_back(rec); } return bannedAddresses; } static UniValue clearbanned(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error("clearbanned\n" "\nClear all banned IPs.\n" "\nExamples:\n" + HelpExampleCli("clearbanned", "") + HelpExampleRpc("clearbanned", "")); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } g_connman->ClearBanned(); return NullUniValue; } static UniValue setnetworkactive(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "setnetworkactive true|false\n" "\nDisable/enable all p2p network activity.\n" "\nArguments:\n" "1. \"state\" (boolean, required) true to " "enable networking, false to disable\n"); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } g_connman->SetNetworkActive(request.params[0].get_bool()); return g_connman->GetNetworkActive(); } // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- { "network", "getconnectioncount", getconnectioncount, {} }, { "network", "ping", ping, {} }, { "network", "getpeerinfo", getpeerinfo, {} }, { "network", "addnode", addnode, {"node","command"} }, { "network", "disconnectnode", disconnectnode, {"address", "nodeid"} }, { "network", "getaddednodeinfo", getaddednodeinfo, {"node"} }, { "network", "getnettotals", getnettotals, {} }, { "network", "getnetworkinfo", getnetworkinfo, {} }, { "network", "setban", setban, {"subnet", "command", "bantime", "absolute"} }, { "network", "listbanned", listbanned, {} }, { "network", "clearbanned", clearbanned, {} }, { "network", "setnetworkactive", setnetworkactive, {"state"} }, }; // clang-format on void RegisterNetRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 354d226ce7..3b2ee66c90 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1,1317 +1,1317 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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/rawtransaction.h" #include "base58.h" #include "chain.h" #include "coins.h" #include "config.h" #include "consensus/validation.h" #include "core_io.h" #include "dstencode.h" #include "init.h" #include "keystore.h" #include "merkleblock.h" #include "net.h" #include "policy/policy.h" #include "primitives/transaction.h" #include "rpc/safemode.h" #include "rpc/server.h" #include "rpc/tojson.h" #include "script/script.h" #include "script/script_error.h" #include "script/sign.h" #include "script/standard.h" #include "txmempool.h" #include "uint256.h" #include "utilstrencodings.h" #include "validation.h" #ifdef ENABLE_WALLET #include "wallet/rpcwallet.h" #endif #include #include void TxToJSON(const CTransaction &tx, const uint256 hashBlock, UniValue &entry) { // Call into TxToUniv() in bitcoin-common to decode the transaction hex. // // Blockchain contextual information (confirmations and blocktime) is not // available to code in bitcoin-common, so we query them here and push the // data into the returned UniValue. TxToUniv(tx, uint256(), entry); if (!hashBlock.IsNull()) { entry.pushKV("blockhash", hashBlock.GetHex()); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex *pindex = (*mi).second; if (chainActive.Contains(pindex)) { entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight); entry.pushKV("time", pindex->GetBlockTime()); entry.pushKV("blocktime", pindex->GetBlockTime()); } else { entry.pushKV("confirmations", 0); } } } } static UniValue getrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getrawtransaction \"txid\" ( verbose )\n" "\nNOTE: By default this function only works for mempool " "transactions. If the -txindex option is\n" "enabled, it also works for blockchain transactions.\n" "DEPRECATED: for now, it also works for transactions with unspent " "outputs.\n" "\nReturn the raw transaction data.\n" "\nIf verbose is 'true', returns an Object with information about " "'txid'.\n" "If verbose is 'false' or omitted, returns a string that is " "serialized, hex-encoded data for 'txid'.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "2. verbose (bool, optional, default=false) If false, return " "a string, otherwise return a json object\n" "\nResult (if verbose is not set or set to false):\n" "\"data\" (string) The serialized, hex-encoded data for " "'txid'\n" "\nResult (if verbose is set to true):\n" "{\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded " "data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as " "provided)\n" " \"hash\" : \"id\", (string) The transaction hash " "(differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The serialized transaction " "size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" " \"vout\": n, (numeric) \n" " \"scriptSig\": { (json object) The script\n" " \"asm\": \"asm\", (string) asm\n" " \"hex\": \"hex\" (string) hex\n" " },\n" " \"sequence\": n (numeric) The script sequence number\n" " }\n" " ,...\n" " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" " \"hex\" : \"hex\", (string) the hex\n" " \"reqSigs\" : n, (numeric) The required sigs\n" " \"type\" : \"pubkeyhash\", (string) The type, eg " "'pubkeyhash'\n" " \"addresses\" : [ (json array of string)\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ]\n" " }\n" " }\n" " ,...\n" " ],\n" " \"blockhash\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The confirmations\n" " \"time\" : ttt, (numeric) The transaction time in " "seconds since epoch (Jan 1 1970 GMT)\n" " \"blocktime\" : ttt (numeric) The block time in seconds " "since epoch (Jan 1 1970 GMT)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getrawtransaction", "\"mytxid\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + HelpExampleRpc("getrawtransaction", "\"mytxid\", true")); } LOCK(cs_main); TxId txid = TxId(ParseHashV(request.params[0], "parameter 1")); // Accept either a bool (true) or a num (>=1) to indicate verbose output. bool fVerbose = false; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { if (request.params[1].isNum()) { if (request.params[1].get_int() != 0) { fVerbose = true; } } else if (request.params[1].isBool()) { if (request.params[1].isTrue()) { fVerbose = true; } } else { throw JSONRPCError( RPC_TYPE_ERROR, "Invalid type provided. Verbose parameter must be a boolean."); } } CTransactionRef tx; uint256 hashBlock; if (!GetTransaction(config, txid, tx, hashBlock, true)) { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" : "No such mempool transaction. Use -txindex " "to enable blockchain transaction queries") + ". Use gettransaction for wallet transactions."); } std::string strHex = EncodeHexTx(*tx, RPCSerializationFlags()); if (!fVerbose) { return strHex; } UniValue result(UniValue::VOBJ); result.pushKV("hex", strHex); TxToJSON(*tx, hashBlock, result); return result; } static UniValue gettxoutproof(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) { throw std::runtime_error( "gettxoutproof [\"txid\",...] ( blockhash )\n" "\nReturns a hex-encoded proof that \"txid\" was included in a " "block.\n" "\nNOTE: By default this function only works sometimes. This is " "when there is an\n" "unspent output in the utxo for this transaction. To make it " "always work,\n" "you need to maintain a transaction index, using the -txindex " "command line option or\n" "specify the block in which the transaction is included manually " "(by blockhash).\n" "\nArguments:\n" "1. \"txids\" (string) A json array of txids to filter\n" " [\n" " \"txid\" (string) A transaction hash\n" " ,...\n" " ]\n" "2. \"blockhash\" (string, optional) If specified, looks for " "txid in the block with this hash\n" "\nResult:\n" "\"data\" (string) A string that is a serialized, " "hex-encoded data for the proof.\n"); } std::set setTxIds; TxId oneTxId; UniValue txids = request.params[0].get_array(); for (unsigned int idx = 0; idx < txids.size(); idx++) { const UniValue &utxid = txids[idx]; if (utxid.get_str().length() != 64 || !IsHex(utxid.get_str())) { throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid txid ") + utxid.get_str()); } TxId txid(uint256S(utxid.get_str())); if (setTxIds.count(txid)) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + utxid.get_str()); } setTxIds.insert(txid); oneTxId = txid; } LOCK(cs_main); CBlockIndex *pblockindex = nullptr; uint256 hashBlock; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { hashBlock = uint256S(request.params[1].get_str()); if (!mapBlockIndex.count(hashBlock)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } pblockindex = mapBlockIndex[hashBlock]; } else { // Loop through txids and try to find which block they're in. Exit loop // once a block is found. for (const auto &txid : setTxIds) { const Coin &coin = AccessByTxid(*pcoinsTip, txid); if (!coin.IsSpent()) { pblockindex = chainActive[coin.GetHeight()]; break; } } } if (pblockindex == nullptr) { CTransactionRef tx; if (!GetTransaction(config, oneTxId, tx, hashBlock, false) || hashBlock.IsNull()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); } if (!mapBlockIndex.count(hashBlock)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); } pblockindex = mapBlockIndex[hashBlock]; } CBlock block; if (!ReadBlockFromDisk(block, pblockindex, config)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); } unsigned int ntxFound = 0; for (const auto &tx : block.vtx) { if (setTxIds.count(tx->GetId())) { ntxFound++; } } if (ntxFound != setTxIds.size()) { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block"); } CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock mb(block, setTxIds); ssMB << mb; std::string strHex = HexStr(ssMB.begin(), ssMB.end()); return strHex; } static UniValue verifytxoutproof(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "verifytxoutproof \"proof\"\n" "\nVerifies that a proof points to a transaction in a block, " "returning the transaction it commits to\n" "and throwing an RPC error if the block is not in our best chain\n" "\nArguments:\n" "1. \"proof\" (string, required) The hex-encoded proof " "generated by gettxoutproof\n" "\nResult:\n" "[\"txid\"] (array, strings) The txid(s) which the proof " "commits to, or empty array if the proof is invalid\n"); } CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock merkleBlock; ssMB >> merkleBlock; UniValue res(UniValue::VARR); std::vector vMatch; std::vector vIndex; if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) { return res; } LOCK(cs_main); if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } for (const uint256 &hash : vMatch) { res.push_back(hash.GetHex()); } return res; } static UniValue createrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { throw std::runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] " "{\"address\":amount,\"data\":\"hex\",...} ( locktime )\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\" (object, required) a json object " "with outputs\n" " {\n" " \"address\": x.xxx, (numeric or string, required) The " "key is the bitcoin address, the numeric value (can be string) is " "the " + CURRENCY_UNIT + " amount\n" " \"data\": \"hex\" (string, required) The key is " "\"data\", the value is hex encoded data\n" " ,...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw " "locktime. Non-0 value also locktime-activates inputs\n" "\nResult:\n" "\"transaction\" (string) hex string of the " "transaction\n" "\nExamples:\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" " "\"{\\\"address\\\":0.01}\"") + 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\\\"}\"")); } RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM}, true); if (request.params[0].isNull() || request.params[1].isNull()) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); } UniValue inputs = request.params[0].get_array(); UniValue sendTo = request.params[1].get_obj(); CMutableTransaction rawTx; if (request.params.size() > 2 && !request.params[2].isNull()) { int64_t nLockTime = request.params[2].get_int64(); if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); } rawTx.nLockTime = nLockTime; } for (size_t idx = 0; idx < inputs.size(); idx++) { const UniValue &input = inputs[idx]; const UniValue &o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); const UniValue &vout_v = find_value(o, "vout"); if (vout_v.isNull()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); } if (!vout_v.isNum()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be a number"); } int nOutput = vout_v.get_int(); if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); } uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); // Set the sequence number if passed in the parameters object. const UniValue &sequenceObj = find_value(o, "sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.get_int64(); if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); } nSequence = uint32_t(seqNr64); } CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); rawTx.vin.push_back(in); } std::set destinations; std::vector addrList = sendTo.getKeys(); for (const std::string &name_ : addrList) { if (name_ == "data") { std::vector data = ParseHexV(sendTo[name_].getValStr(), "Data"); CTxOut out(Amount::zero(), CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { CTxDestination destination = DecodeDestination(name_, config.GetChainParams()); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); } if (!destinations.insert(destination).second) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); } CScript scriptPubKey = GetScriptForDestination(destination); Amount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); } } return EncodeHexTx(CTransaction(rawTx)); } static UniValue decoderawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "decoderawtransaction \"hexstring\"\n" "\nReturn a JSON object representing the serialized, hex-encoded " "transaction.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex " "string\n" "\nResult:\n" "{\n" " \"txid\" : \"id\", (string) The transaction id\n" " \"hash\" : \"id\", (string) The transaction hash " "(differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" " \"vout\": n, (numeric) The output number\n" " \"scriptSig\": { (json object) The script\n" " \"asm\": \"asm\", (string) asm\n" " \"hex\": \"hex\" (string) hex\n" " },\n" " \"sequence\": n (numeric) The script sequence number\n" " }\n" " ,...\n" " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" " \"hex\" : \"hex\", (string) the hex\n" " \"reqSigs\" : n, (numeric) The required sigs\n" " \"type\" : \"pubkeyhash\", (string) The type, eg " "'pubkeyhash'\n" " \"addresses\" : [ (json array of string)\n" " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) " "bitcoin address\n" " ,...\n" " ]\n" " }\n" " }\n" " ,...\n" " ],\n" "}\n" "\nExamples:\n" + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + HelpExampleRpc("decoderawtransaction", "\"hexstring\"")); } LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR}); CMutableTransaction mtx; if (!DecodeHexTx(mtx, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } UniValue result(UniValue::VOBJ); TxToUniv(CTransaction(std::move(mtx)), uint256(), result); return result; } static UniValue decodescript(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "decodescript \"hexstring\"\n" "\nDecode a hex-encoded script.\n" "\nArguments:\n" "1. \"hexstring\" (string) the hex encoded script\n" "\nResult:\n" "{\n" " \"asm\":\"asm\", (string) Script public key\n" " \"hex\":\"hex\", (string) hex encoded public key\n" " \"type\":\"type\", (string) The output type\n" " \"reqSigs\": n, (numeric) The required signatures\n" " \"addresses\": [ (json array of string)\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ],\n" " \"p2sh\",\"address\" (string) address of P2SH script wrapping " "this redeem script (not returned if the script is already a " "P2SH).\n" "}\n" "\nExamples:\n" + HelpExampleCli("decodescript", "\"hexstring\"") + HelpExampleRpc("decodescript", "\"hexstring\"")); } RPCTypeCheck(request.params, {UniValue::VSTR}); UniValue r(UniValue::VOBJ); CScript script; if (request.params[0].get_str().size() > 0) { std::vector scriptData( ParseHexV(request.params[0], "argument")); script = CScript(scriptData.begin(), scriptData.end()); } else { // Empty scripts are valid. } ScriptPubKeyToUniv(script, r, false); UniValue type; type = find_value(r, "type"); if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. r.pushKV("p2sh", EncodeDestination(CScriptID(script))); } return r; } /** * Pushes a JSON object for script verification or signing errors to vErrorsRet. */ static void TxInErrorToJSON(const CTxIn &txin, UniValue &vErrorsRet, const std::string &strMessage) { UniValue entry(UniValue::VOBJ); entry.pushKV("txid", txin.prevout.GetTxId().ToString()); entry.pushKV("vout", uint64_t(txin.prevout.GetN())); entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); entry.pushKV("sequence", uint64_t(txin.nSequence)); entry.pushKV("error", strMessage); vErrorsRet.push_back(entry); } static UniValue combinerawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "combinerawtransaction [\"hexstring\",...]\n" "\nCombine multiple partially signed transactions into one " "transaction.\n" "The combined transaction may be another partially signed " "transaction or a \n" "fully signed transaction." "\nArguments:\n" "1. \"txs\" (string) A json array of hex strings of " "partially signed transactions\n" " [\n" " \"hexstring\" (string) A transaction hash\n" " ,...\n" " ]\n" "\nResult:\n" "\"hex\" : \"value\", (string) The hex-encoded raw " "transaction with signature(s)\n" "\nExamples:\n" + HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]")); } UniValue txs = request.params[0].get_array(); std::vector txVariants(txs.size()); for (unsigned int idx = 0; idx < txs.size(); idx++) { if (!DecodeHexTx(txVariants[idx], txs[idx].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed for tx %d", idx)); } } if (txVariants.empty()) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transactions"); } // mergedTx will end up with all the signatures; it // starts as a clone of the rawtx: CMutableTransaction mergedTx(txVariants[0]); // Fetch previous transactions (inputs): CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { LOCK(cs_main); LOCK(g_mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, g_mempool); // temporarily switch cache backend to db+mempool view view.SetBackend(viewMempool); for (const CTxIn &txin : mergedTx.vin) { // Load entries from viewChain into view; can fail. view.AccessCoin(txin.prevout); } // switch back to avoid locking mempool for too long view.SetBackend(viewDummy); } // Use CTransaction for the constant parts of the // transaction to avoid rehashing. const CTransaction txConst(mergedTx); // Sign what we can: for (size_t i = 0; i < mergedTx.vin.size(); i++) { CTxIn &txin = mergedTx.vin[i]; const Coin &coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent"); } const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; const Amount &amount = coin.GetTxOut().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)); } } UpdateTransaction(mergedTx, i, sigdata); } return EncodeHexTx(CTransaction(mergedTx)); } UniValue SignTransaction(CMutableTransaction &mtx, const UniValue &prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue &hashType) { // Fetch previous transactions (inputs): CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { LOCK2(cs_main, g_mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, g_mempool); // Temporarily switch cache backend to db+mempool view. view.SetBackend(viewMempool); for (const CTxIn &txin : mtx.vin) { // Load entries from viewChain into view; can fail. view.AccessCoin(txin.prevout); } // Switch back to avoid locking mempool for too long. view.SetBackend(viewDummy); } // Add previous txouts given in the RPC call: if (!prevTxsUnival.isNull()) { UniValue prevTxs = prevTxsUnival.get_array(); for (size_t idx = 0; idx < prevTxs.size(); ++idx) { const UniValue &p = prevTxs[idx]; if (!p.isObject()) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with " "{\"txid'\",\"vout\",\"scriptPubKey\"}"); } UniValue prevOut = p.get_obj(); RPCTypeCheckObj(prevOut, { {"txid", UniValueType(UniValue::VSTR)}, {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, // "amount" is also required but check is done // below due to UniValue::VNUM erroneously // not accepting quoted numerics // (which are valid JSON) }); uint256 txid = ParseHashO(prevOut, "txid"); int nOut = find_value(prevOut, "vout").get_int(); if (nOut < 0) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); } COutPoint out(txid, nOut); std::vector pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { const Coin &coin = view.AccessCoin(out); if (!coin.IsSpent() && coin.GetTxOut().scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) + "\nvs:\n" + ScriptToAsmStr(scriptPubKey); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } CTxOut txout; txout.scriptPubKey = scriptPubKey; txout.nValue = Amount::zero(); if (prevOut.exists("amount")) { txout.nValue = AmountFromValue(find_value(prevOut, "amount")); } else { // amount param is required in replay-protected txs. // Note that we must check for its presence here rather // than use RPCTypeCheckObj() above, since UniValue::VNUM // parser incorrectly parses numerics with quotes, eg // "3.12" as a string when JSON allows it to also parse // as numeric. And we have to accept numerics with quotes // because our own dogfood (our rpc results) always // produces decimal numbers that are quoted // eg getbalance returns "3.14152" rather than 3.14152 throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); } view.AddCoin(out, Coin(txout, 1, false), true); } // If redeemScript given and not using the local wallet (private // keys given), add redeemScript to the keystore so it can be // signed: if (is_temp_keystore && scriptPubKey.IsPayToScriptHash()) { RPCTypeCheckObj( prevOut, { {"txid", UniValueType(UniValue::VSTR)}, {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, {"redeemScript", UniValueType(UniValue::VSTR)}, }); UniValue v = find_value(prevOut, "redeemScript"); if (!v.isNull()) { std::vector rsData(ParseHexV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); keystore->AddCScript(redeemScript); } } } } SigHashType sigHashType = SigHashType().withForkId(); if (!hashType.isNull()) { static std::map mapSigHashValues = { {"ALL", SIGHASH_ALL}, {"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY}, {"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID}, {"ALL|FORKID|ANYONECANPAY", SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, {"NONE", SIGHASH_NONE}, {"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY}, {"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID}, {"NONE|FORKID|ANYONECANPAY", SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, {"SINGLE", SIGHASH_SINGLE}, {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY}, {"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID}, {"SINGLE|FORKID|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, }; std::string strHashType = hashType.get_str(); if (!mapSigHashValues.count(strHashType)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); } sigHashType = SigHashType(mapSigHashValues[strHashType]); if (!sigHashType.hasForkId()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Signature must use SIGHASH_FORKID"); } } // Script verification errors. UniValue vErrors(UniValue::VARR); // Use CTransaction for the constant parts of the transaction to avoid // rehashing. const CTransaction txConst(mtx); // Sign what we can: for (size_t i = 0; i < mtx.vin.size(); i++) { CTxIn &txin = mtx.vin[i]; const Coin &coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); continue; } const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; const Amount amount = coin.GetTxOut().nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || (i < mtx.vout.size())) { ProduceSignature(MutableTransactionSignatureCreator( keystore, &mtx, i, amount, sigHashType), prevPubKey, sigdata); } sigdata = CombineSignatures( prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i)); UpdateTransaction(mtx, i, sigdata); ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript( txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { // Unable to sign input and verification failed (possible // attempt to partially sign). TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid " "stack size (possibly missing " "key)"); } else { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } } bool fComplete = vErrors.empty(); UniValue result(UniValue::VOBJ); result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); result.pushKV("complete", fComplete); if (!vErrors.empty()) { result.pushKV("errors", vErrors); } return result; } static UniValue signrawtransactionwithkey(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) { throw std::runtime_error( "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( " "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\"," "\"redeemScript\":\"hex\"},...] sighashtype )\n" "\nSign inputs for raw transaction (serialized, hex-encoded).\n" "The second argument is an array of base58-encoded private\n" "keys that will be the only keys used to sign the transaction.\n" "The third optional argument (may be null) is an array of previous " "transaction outputs that\n" "this transaction depends on but may not yet be in the block " "chain.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The " "transaction hex string\n" "2. \"privkeys\" (string, required) A json " "array of base58-encoded private keys for signing\n" " [ (json array of strings)\n" " \"privatekey\" (string) private key in " "base58-encoding\n" " ,...\n" " ]\n" "3. \"prevtxs\" (string, optional) An json " "array of previous dependent transaction outputs\n" " [ (json array of json objects, " "or 'null' if none provided)\n" " {\n" " \"txid\":\"id\", (string, required) The " "transaction id\n" " \"vout\":n, (numeric, required) The " "output number\n" " \"scriptPubKey\": \"hex\", (string, required) script " "key\n" " \"redeemScript\": \"hex\", (string, required for " "P2SH) redeem script\n" " \"amount\": value (numeric, required) The " "amount spent\n" " }\n" " ,...\n" " ]\n" "4. \"sighashtype\" (string, optional, " "default=ALL) The signature hash type. Must be one of\n" " \"ALL|FORKID\"\n" " \"NONE|FORKID\"\n" " \"SINGLE|FORKID\"\n" " \"ALL|FORKID|ANYONECANPAY\"\n" " \"NONE|FORKID|ANYONECANPAY\"\n" " \"SINGLE|FORKID|ANYONECANPAY\"\n" "\nResult:\n" "{\n" " \"hex\" : \"value\", (string) The hex-encoded " "raw transaction with signature(s)\n" " \"complete\" : true|false, (boolean) If the " "transaction has a complete set of signatures\n" " \"errors\" : [ (json array of objects) " "Script verification errors (if there are any)\n" " {\n" " \"txid\" : \"hash\", (string) The hash of the " "referenced, previous transaction\n" " \"vout\" : n, (numeric) The index of the " "output to spent and used as input\n" " \"scriptSig\" : \"hex\", (string) The hex-encoded " "signature script\n" " \"sequence\" : n, (numeric) Script sequence " "number\n" " \"error\" : \"text\" (string) Verification or " "signing error related to the input\n" " }\n" " ,...\n" " ]\n" "}\n" "\nExamples:\n" + HelpExampleCli("signrawtransactionwithkey", "\"myhex\"") + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\"")); } RPCTypeCheck( request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); CMutableTransaction mtx; if (!DecodeHexTx(mtx, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } CBasicKeyStore keystore; const UniValue &keys = request.params[1].get_array(); for (size_t idx = 0; idx < keys.size(); ++idx) { UniValue k = keys[idx]; CBitcoinSecret vchSecret; if (!vchSecret.SetString(k.get_str())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); } CKey key = vchSecret.GetKey(); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); } keystore.AddKey(key); } return SignTransaction(mtx, request.params[2], &keystore, true, request.params[3]); } static UniValue signrawtransaction(const Config &config, const JSONRPCRequest &request) { #ifdef ENABLE_WALLET CWallet *const pwallet = GetWalletForJSONRPCRequest(request); #endif if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) { throw std::runtime_error( "signrawtransaction \"hexstring\" ( " "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\"," "\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype " ")\n" "\nSign inputs for raw transaction (serialized, hex-encoded).\n" "The second optional argument (may be null) is an array of " "previous transaction outputs that\n" "this transaction depends on but may not yet be in the block " "chain.\n" "The third optional argument (may be null) is an array of " "base58-encoded private\n" "keys that, if given, will be the only keys used to sign the " "transaction.\n" #ifdef ENABLE_WALLET + HelpRequiringPassphrase(pwallet) + "\n" #endif "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex " "string\n" "2. \"prevtxs\" (string, optional) An json array of previous " "dependent transaction outputs\n" " [ (json array of json objects, or 'null' if " "none provided)\n" " {\n" " \"txid\":\"id\", (string, required) The " "transaction id\n" " \"vout\":n, (numeric, required) The " "output number\n" " \"scriptPubKey\": \"hex\", (string, required) script " "key\n" " \"redeemScript\": \"hex\", (string, required for P2SH) " "redeem script\n" " \"amount\": value (numeric, required) The " "amount spent\n" " }\n" " ,...\n" " ]\n" "3. \"privkeys\" (string, optional) A json array of " "base58-encoded private keys for signing\n" " [ (json array of strings, or 'null' if none " "provided)\n" " \"privatekey\" (string) private key in base58-encoding\n" " ,...\n" " ]\n" "4. \"sighashtype\" (string, optional, default=ALL) The " "signature hash type. Must be one of\n" " \"ALL|FORKID\"\n" " \"NONE|FORKID\"\n" " \"SINGLE|FORKID\"\n" " \"ALL|FORKID|ANYONECANPAY\"\n" " \"NONE|FORKID|ANYONECANPAY\"\n" " \"SINGLE|FORKID|ANYONECANPAY\"\n" "\nResult:\n" "{\n" " \"hex\" : \"value\", (string) The hex-encoded raw " "transaction with signature(s)\n" " \"complete\" : true|false, (boolean) If the transaction has a " "complete set of signatures\n" " \"errors\" : [ (json array of objects) Script " "verification errors (if there are any)\n" " {\n" " \"txid\" : \"hash\", (string) The hash of the " "referenced, previous transaction\n" " \"vout\" : n, (numeric) The index of the " "output to spent and used as input\n" " \"scriptSig\" : \"hex\", (string) The hex-encoded " "signature script\n" " \"sequence\" : n, (numeric) Script sequence " "number\n" " \"error\" : \"text\" (string) Verification or " "signing error related to the input\n" " }\n" " ,...\n" " ]\n" "}\n" "\nExamples:\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + HelpExampleRpc("signrawtransaction", "\"myhex\"")); } RPCTypeCheck( request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); // Make a JSONRPCRequest to pass on to the right signrawtransaction* command JSONRPCRequest new_request; new_request.id = request.id; new_request.params.setArray(); // For signing with private keys if (!request.params[2].isNull()) { new_request.params.push_back(request.params[0]); // Note: the prevtxs and privkeys are reversed for // signrawtransactionwithkey new_request.params.push_back(request.params[2]); new_request.params.push_back(request.params[1]); new_request.params.push_back(request.params[3]); return signrawtransactionwithkey(config, new_request); } // Otherwise sign with the wallet which does not take a privkeys parameter #ifdef ENABLE_WALLET else { new_request.params.push_back(request.params[0]); new_request.params.push_back(request.params[1]); new_request.params.push_back(request.params[3]); return signrawtransactionwithwallet(config, new_request); } #endif // If we have made it this far, then wallet is disabled and no private keys // were given, so fail here. throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available."); } static UniValue sendrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "sendrawtransaction \"hexstring\" ( allowhighfees )\n" "\nSubmits raw transaction (serialized, hex-encoded) to local node " "and network.\n" "\nAlso see createrawtransaction and signrawtransaction calls.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The hex string of the raw " "transaction)\n" "2. allowhighfees (boolean, optional, default=false) Allow high " "fees\n" "\nResult:\n" "\"hex\" (string) The transaction hash in hex\n" "\nExamples:\n" "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : " "\\\"mytxid\\\",\\\"vout\\\":0}]\" " "\"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + "\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + "\nAs a json rpc call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")); } ObserveSafeMode(); LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); // parse hex string from parameter CMutableTransaction mtx; if (!DecodeHexTx(mtx, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const uint256 &txid = tx->GetId(); bool fLimitFree = false; Amount nMaxRawTxFee = maxTxFee; if (request.params.size() > 1 && request.params[1].get_bool()) { nMaxRawTxFee = Amount::zero(); } CCoinsViewCache &view = *pcoinsTip; bool fHaveChain = false; for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { const Coin &existingCoin = view.AccessCoin(COutPoint(txid, o)); fHaveChain = !existingCoin.IsSpent(); } bool fHaveMempool = g_mempool.exists(txid); if (!fHaveMempool && !fHaveChain) { // Push to local node and sync with wallets. CValidationState state; bool fMissingInputs; if (!AcceptToMemoryPool(config, g_mempool, state, std::move(tx), fLimitFree, &fMissingInputs, false, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { if (fMissingInputs) { throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); } throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); } } } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } CInv inv(MSG_TX, txid); g_connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); return txid.GetHex(); } // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose"} }, { "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decodescript", decodescript, {"hexstring"} }, { "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} }, { "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} }, { "rawtransactions", "signrawtransaction", signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ { "rawtransactions", "signrawtransactionwithkey", signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, { "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} }, { "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} }, }; // clang-format on void RegisterRawTransactionRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 9c53355b42..2a8997c578 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1,1432 +1,1432 @@ // Copyright (c) 2009-2016 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 "base58.h" #include "chain.h" #include "config.h" #include "core_io.h" #include "dstencode.h" #include "init.h" #include "merkleblock.h" #include "rpc/safemode.h" #include "rpc/server.h" #include "rpcwallet.h" #include "script/script.h" #include "script/standard.h" #include "sync.h" #include "util.h" #include "utiltime.h" #include "validation.h" #include "wallet.h" #include #include #include #include #include #include static int64_t DecodeDumpTime(const std::string &str) { static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0); static const std::locale loc( std::locale::classic(), new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ")); std::istringstream iss(str); iss.imbue(loc); boost::posix_time::ptime ptime(boost::date_time::not_a_date_time); iss >> ptime; if (ptime.is_not_a_date_time()) return 0; return (ptime - epoch).total_seconds(); } static std::string EncodeDumpString(const std::string &str) { std::stringstream ret; for (uint8_t c : str) { if (c <= 32 || c >= 128 || c == '%') { ret << '%' << HexStr(&c, &c + 1); } else { ret << c; } } return ret.str(); } std::string DecodeDumpString(const std::string &str) { std::stringstream ret; for (unsigned int pos = 0; pos < str.length(); pos++) { uint8_t c = str[pos]; if (c == '%' && pos + 2 < str.length()) { c = (((str[pos + 1] >> 6) * 9 + ((str[pos + 1] - '0') & 15)) << 4) | ((str[pos + 2] >> 6) * 9 + ((str[pos + 2] - '0') & 15)); pos += 2; } ret << c; } return ret.str(); } UniValue importprivkey(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n" "\nAdds a private key (as returned by dumpprivkey) to your wallet. " "Requires a new wallet backup.\n" "\nArguments:\n" "1. \"bitcoinprivkey\" (string, required) The private key (see " "dumpprivkey)\n" "2. \"label\" (string, optional, default=\"\") An " "optional label\n" "3. rescan (boolean, optional, default=true) Rescan " "the wallet for transactions\n" "\nNote: This call can take minutes to complete if rescan is " "true.\n" "\nExamples:\n" "\nDump a private key\n" + HelpExampleCli("dumpprivkey", "\"myaddress\"") + "\nImport the private key with rescan\n" + HelpExampleCli("importprivkey", "\"mykey\"") + "\nImport using a label and without rescan\n" + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") + "\nImport using default blank label and without rescan\n" + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")); LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); std::string strSecret = request.params[0].get_str(); std::string strLabel = ""; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { strLabel = request.params[1].get_str(); } // Whether to perform rescan after import bool fRescan = true; - if (request.params.size() > 2) { + if (!request.params[2].isNull()) { fRescan = request.params[2].get_bool(); } if (fRescan && fPruneMode) { throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); } CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(strSecret); if (!fGood) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); } CKey key = vchSecret.GetKey(); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); } CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); CKeyID vchAddress = pubkey.GetID(); { pwallet->MarkDirty(); pwallet->SetAddressBook(vchAddress, strLabel, "receive"); // Don't throw error in case a key is already there if (pwallet->HaveKey(vchAddress)) { return NullUniValue; } pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1; if (!pwallet->AddKeyPubKey(key, pubkey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } // whenever a key is imported, we need to scan the whole chain pwallet->UpdateTimeFirstKey(1); if (fRescan) { pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */); } } return NullUniValue; } UniValue abortrescan(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 0) { throw std::runtime_error("abortrescan\n" "\nStops current wallet rescan triggered e.g. " "by an importprivkey call.\n" "\nExamples:\n" "\nImport a private key\n" + HelpExampleCli("importprivkey", "\"mykey\"") + "\nAbort the running wallet rescan\n" + HelpExampleCli("abortrescan", "") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("abortrescan", "")); } ObserveSafeMode(); if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) { return false; } pwallet->AbortRescan(); return true; } void ImportAddress(CWallet *, const CTxDestination &dest, const std::string &strLabel); void ImportScript(CWallet *const pwallet, const CScript &script, const std::string &strLabel, bool isRedeemScript) { if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the " "private key for this address or " "script"); } pwallet->MarkDirty(); if (!pwallet->HaveWatchOnly(script) && !pwallet->AddWatchOnly(script, 0 /* nCreateTime */)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } if (isRedeemScript) { if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } ImportAddress(pwallet, CScriptID(script), strLabel); } else { CTxDestination destination; if (ExtractDestination(script, destination)) { pwallet->SetAddressBook(destination, strLabel, "receive"); } } } void ImportAddress(CWallet *const pwallet, const CTxDestination &dest, const std::string &strLabel) { CScript script = GetScriptForDestination(dest); ImportScript(pwallet, script, strLabel, false); // add to address book or update label if (IsValidDestination(dest)) { pwallet->SetAddressBook(dest, strLabel, "receive"); } } UniValue importaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) { throw std::runtime_error( "importaddress \"address\" ( \"label\" rescan p2sh )\n" "\nAdds a script (in hex) or address that can be watched as if it " "were in your wallet but cannot be used to spend. Requires a new " "wallet backup.\n" "\nArguments:\n" "1. \"script\" (string, required) The hex-encoded script " "(or address)\n" "2. \"label\" (string, optional, default=\"\") An " "optional label\n" "3. rescan (boolean, optional, default=true) Rescan " "the wallet for transactions\n" "4. p2sh (boolean, optional, default=false) Add " "the P2SH version of the script as well\n" "\nNote: This call can take minutes to complete if rescan is " "true.\n" "If you have the full public key, you should call importpubkey " "instead of this.\n" "\nNote: If you import a non-standard raw script in hex form, " "outputs sending to it will be treated\n" "as change, and not show up in many RPCs.\n" "\nExamples:\n" "\nImport a script with rescan\n" + HelpExampleCli("importaddress", "\"myscript\"") + "\nImport using a label without rescan\n" + HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false")); } std::string strLabel = ""; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { strLabel = request.params[1].get_str(); } // Whether to perform rescan after import bool fRescan = true; - if (request.params.size() > 2) { + if (!request.params[2].isNull()) { fRescan = request.params[2].get_bool(); } if (fRescan && fPruneMode) { throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); } // Whether to import a p2sh version, too bool fP2SH = false; - if (request.params.size() > 3) { + if (!request.params[3].isNull()) { fP2SH = request.params[3].get_bool(); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (IsValidDestination(dest)) { if (fP2SH) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use " "a script instead"); } ImportAddress(pwallet, dest, strLabel); } else if (IsHex(request.params[0].get_str())) { std::vector data(ParseHex(request.params[0].get_str())); ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script"); } if (fRescan) { pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwallet->ReacceptWalletTransactions(); } return NullUniValue; } UniValue importprunedfunds(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "importprunedfunds\n" "\nImports funds without rescan. Corresponding address or script " "must previously be included in wallet. Aimed towards pruned " "wallets. The end-user is responsible to import additional " "transactions that subsequently spend the imported outputs or " "rescan after the point in the blockchain the transaction is " "included.\n" "\nArguments:\n" "1. \"rawtransaction\" (string, required) A raw transaction in hex " "funding an already-existing address in wallet\n" "2. \"txoutproof\" (string, required) The hex output from " "gettxoutproof that contains the transaction\n"); } CMutableTransaction tx; if (!DecodeHexTx(tx, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } uint256 txid = tx.GetId(); CWalletTx wtx(pwallet, MakeTransactionRef(std::move(tx))); CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock merkleBlock; ssMB >> merkleBlock; // Search partial merkle tree in proof for our transaction and index in // valid block std::vector vMatch; std::vector vIndex; unsigned int txnIndex = 0; if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) { LOCK(cs_main); if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains( mapBlockIndex[merkleBlock.header.GetHash()])) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } std::vector::const_iterator it; if ((it = std::find(vMatch.begin(), vMatch.end(), txid)) == vMatch.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof"); } txnIndex = vIndex[it - vMatch.begin()]; } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock"); } wtx.nIndex = txnIndex; wtx.hashBlock = merkleBlock.header.GetHash(); LOCK2(cs_main, pwallet->cs_wallet); if (pwallet->IsMine(wtx)) { pwallet->AddToWallet(wtx, false); return NullUniValue; } throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction"); } UniValue removeprunedfunds(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "removeprunedfunds \"txid\"\n" "\nDeletes the specified transaction from the wallet. Meant for " "use with pruned wallets and as a companion to importprunedfunds. " "This will effect wallet balances.\n" "\nArguments:\n" "1. \"txid\" (string, required) The hex-encoded id of " "the transaction you are deleting\n" "\nExamples:\n" + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1" "ce581bebf46446a512166eae762873" "4ea0a5\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("removprunedfunds", "\"a8d0c0184dde994a09ec054286f1c" "e581bebf46446a512166eae7628734e" "a0a5\"")); } LOCK2(cs_main, pwallet->cs_wallet); TxId txid; txid.SetHex(request.params[0].get_str()); std::vector txIds; txIds.push_back(txid); std::vector txIdsOut; if (pwallet->ZapSelectTx(txIds, txIdsOut) != DB_LOAD_OK) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction."); } if (txIdsOut.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet."); } return NullUniValue; } UniValue importpubkey(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) { throw std::runtime_error( "importpubkey \"pubkey\" ( \"label\" rescan )\n" "\nAdds a public key (in hex) that can be watched as if it were in " "your wallet but cannot be used to spend. Requires a new wallet " "backup.\n" "\nArguments:\n" "1. \"pubkey\" (string, required) The hex-encoded public " "key\n" "2. \"label\" (string, optional, default=\"\") An " "optional label\n" "3. rescan (boolean, optional, default=true) Rescan " "the wallet for transactions\n" "\nNote: This call can take minutes to complete if rescan is " "true.\n" "\nExamples:\n" "\nImport a public key with rescan\n" + HelpExampleCli("importpubkey", "\"mypubkey\"") + "\nImport using a label without rescan\n" + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")); } std::string strLabel = ""; - if (request.params.size() > 1) { + if (!request.params[1].isNull()) { strLabel = request.params[1].get_str(); } // Whether to perform rescan after import bool fRescan = true; - if (request.params.size() > 2) { + if (!request.params[2].isNull()) { fRescan = request.params[2].get_bool(); } if (fRescan && fPruneMode) { throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); } if (!IsHex(request.params[0].get_str())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); } std::vector data(ParseHex(request.params[0].get_str())); CPubKey pubKey(data.begin(), data.end()); if (!pubKey.IsFullyValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); } LOCK2(cs_main, pwallet->cs_wallet); ImportAddress(pwallet, pubKey.GetID(), strLabel); ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false); if (fRescan) { pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwallet->ReacceptWalletTransactions(); } return NullUniValue; } UniValue importwallet(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "importwallet \"filename\"\n" "\nImports keys from a wallet dump file (see dumpwallet). Requires " "a new wallet backup to include imported keys.\n" "\nArguments:\n" "1. \"filename\" (string, required) The wallet file\n" "\nExamples:\n" "\nDump the wallet\n" + HelpExampleCli("dumpwallet", "\"test\"") + "\nImport the wallet\n" + HelpExampleCli("importwallet", "\"test\"") + "\nImport using the json rpc call\n" + HelpExampleRpc("importwallet", "\"test\"")); } if (fPruneMode) { throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode"); } LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); std::ifstream file; file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate); if (!file.is_open()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); } int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); bool fGood = true; int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); // show progress dialog in GUI pwallet->ShowProgress(_("Importing..."), 0); while (file.good()) { pwallet->ShowProgress( "", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); std::string line; std::getline(file, line); if (line.empty() || line[0] == '#') { continue; } std::vector vstr; boost::split(vstr, line, boost::is_any_of(" ")); if (vstr.size() < 2) { continue; } CBitcoinSecret vchSecret; if (vchSecret.SetString(vstr[0])) { CKey key = vchSecret.GetKey(); CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); CKeyID keyid = pubkey.GetID(); if (pwallet->HaveKey(keyid)) { LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); continue; } int64_t nTime = DecodeDumpTime(vstr[1]); std::string strLabel; bool fLabel = true; for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { if (boost::algorithm::starts_with(vstr[nStr], "#")) { break; } if (vstr[nStr] == "change=1") { fLabel = false; } if (vstr[nStr] == "reserve=1") { fLabel = false; } if (boost::algorithm::starts_with(vstr[nStr], "label=")) { strLabel = DecodeDumpString(vstr[nStr].substr(6)); fLabel = true; } } LogPrintf("Importing %s...\n", EncodeDestination(keyid)); if (!pwallet->AddKeyPubKey(key, pubkey)) { fGood = false; continue; } pwallet->mapKeyMetadata[keyid].nCreateTime = nTime; if (fLabel) { pwallet->SetAddressBook(keyid, strLabel, "receive"); } nTimeBegin = std::min(nTimeBegin, nTime); } else if (IsHex(vstr[0])) { std::vector vData(ParseHex(vstr[0])); CScript script = CScript(vData.begin(), vData.end()); if (pwallet->HaveCScript(script)) { LogPrintf("Skipping import of %s (script already present)\n", vstr[0]); continue; } if (!pwallet->AddCScript(script)) { LogPrintf("Error importing script %s\n", vstr[0]); fGood = false; continue; } int64_t birth_time = DecodeDumpTime(vstr[1]); if (birth_time > 0) { pwallet->m_script_metadata[CScriptID(script)].nCreateTime = birth_time; nTimeBegin = std::min(nTimeBegin, birth_time); } } } file.close(); // hide progress dialog in GUI pwallet->ShowProgress("", 100); pwallet->UpdateTimeFirstKey(nTimeBegin); pwallet->RescanFromTime(nTimeBegin, false /* update */); pwallet->MarkDirty(); if (!fGood) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet"); } return NullUniValue; } UniValue dumpprivkey(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "dumpprivkey \"address\"\n" "\nReveals the private key corresponding to 'address'.\n" "Then the importprivkey can be used with this output\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for the " "private key\n" "\nResult:\n" "\"key\" (string) The private key\n" "\nExamples:\n" + HelpExampleCli("dumpprivkey", "\"myaddress\"") + HelpExampleCli("importprivkey", "\"mykey\"") + HelpExampleRpc("dumpprivkey", "\"myaddress\"")); } LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); CTxDestination dest = DecodeDestination(strAddress, config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } const CKeyID *keyID = boost::get(&dest); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); } CKey vchSecret; if (!pwallet->GetKey(*keyID, vchSecret)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); } return CBitcoinSecret(vchSecret).ToString(); } UniValue dumpwallet(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "dumpwallet \"filename\"\n" "\nDumps all wallet keys in a human-readable format to a " "server-side file. This does not allow overwriting existing " "files.\n" "Imported scripts are included in the dumpsfile, but corresponding " "addresses may not be added automatically by importwallet.\n" "Note that if your wallet contains keys which are not derived from " "your HD seed (e.g. imported keys), these are not covered by\n" "only backing up the seed itself, and must be backed up too (e.g. " "ensure you back up the whole dumpfile).\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename with path " "(either absolute or relative to bitcoind)\n" "\nResult:\n" "{ (json object)\n" " \"filename\" : { (string) The filename with full " "absolute path\n" "}\n" "\nExamples:\n" + HelpExampleCli("dumpwallet", "\"test\"") + HelpExampleRpc("dumpwallet", "\"test\"")); LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); fs::path filepath = request.params[0].get_str(); filepath = fs::absolute(filepath); /** * Prevent arbitrary files from being overwritten. There have been reports * that users have overwritten wallet files this way: * https://github.com/bitcoin/bitcoin/issues/9934 * It may also avoid other security issues. */ if (fs::exists(filepath)) { throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are " "sure this is what you want, " "move it out of the way first"); } std::ofstream file; file.open(filepath.string().c_str()); if (!file.is_open()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); } std::map mapKeyBirth; const std::map &mapKeyPool = pwallet->GetAllReserveKeys(); pwallet->GetKeyBirthTimes(mapKeyBirth); std::set scripts = pwallet->GetCScripts(); // TODO: include scripts in GetKeyBirthTimes() output instead of separate // sort time/key pairs std::vector> vKeyBirth; for (const auto &entry : mapKeyBirth) { if (const CKeyID *keyID = boost::get(&entry.first)) { // set and test vKeyBirth.push_back(std::make_pair(entry.second, *keyID)); } } mapKeyBirth.clear(); std::sort(vKeyBirth.begin(), vKeyBirth.end()); // produce output file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD); file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime())); file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); file << strprintf("# mined on %s\n", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime())); file << "\n"; // add the base58check encoded extended master if the wallet uses HD CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID; if (!masterKeyID.IsNull()) { CKey key; if (pwallet->GetKey(masterKeyID, key)) { CExtKey masterKey; masterKey.SetMaster(key.begin(), key.size()); CBitcoinExtKey b58extkey; b58extkey.SetKey(masterKey); file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n"; } } for (std::vector>::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; std::string strTime = FormatISO8601DateTime(it->first); std::string strAddr = EncodeDestination(keyid); CKey key; if (pwallet->GetKey(keyid, key)) { file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); if (pwallet->mapAddressBook.count(keyid)) { file << strprintf( "label=%s", EncodeDumpString(pwallet->mapAddressBook[keyid].name)); } else if (keyid == masterKeyID) { file << "hdmaster=1"; } else if (mapKeyPool.count(keyid)) { file << "reserve=1"; } else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") { file << "inactivehdmaster=1"; } else { file << "change=1"; } file << strprintf( " # addr=%s%s\n", strAddr, (pwallet->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath=" + pwallet->mapKeyMetadata[keyid].hdKeypath : "")); } } file << "\n"; for (const CScriptID &scriptid : scripts) { CScript script; std::string create_time = "0"; std::string address = EncodeDestination(scriptid); // get birth times for scripts with metadata auto it = pwallet->m_script_metadata.find(scriptid); if (it != pwallet->m_script_metadata.end()) { create_time = FormatISO8601DateTime(it->second.nCreateTime); } if (pwallet->GetCScript(scriptid, script)) { file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time); file << strprintf(" # addr=%s\n", address); } } file << "\n"; file << "# End of dump\n"; file.close(); UniValue reply(UniValue::VOBJ); reply.pushKV("filename", filepath.string()); return reply; } UniValue ProcessImport(CWallet *const pwallet, const UniValue &data, const int64_t timestamp) { try { bool success = false; // Required fields. const UniValue &scriptPubKey = data["scriptPubKey"]; // Should have script or JSON with "address". if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey"); } // Optional fields. const std::string &strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : ""; const UniValue &pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue(); const UniValue &keys = data.exists("keys") ? data["keys"].get_array() : UniValue(); const bool internal = data.exists("internal") ? data["internal"].get_bool() : false; const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false; const std::string &label = data.exists("label") && !internal ? data["label"].get_str() : ""; bool isScript = scriptPubKey.getType() == UniValue::VSTR; bool isP2SH = strRedeemScript.length() > 0; const std::string &output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str(); // Parse the output. CScript script; CTxDestination dest; if (!isScript) { dest = DecodeDestination(output, pwallet->chainParams); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } script = GetScriptForDestination(dest); } else { if (!IsHex(output)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey"); } std::vector vData(ParseHex(output)); script = CScript(vData.begin(), vData.end()); } // Watchonly and private keys if (watchOnly && keys.size()) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys"); } // Internal + Label if (internal && data.exists("label")) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Incompatibility found between internal and label"); } // Not having Internal + Script if (!internal && isScript) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey"); } // Keys / PubKeys size check. if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address"); } // Invalid P2SH redeemScript if (isP2SH && !IsHex(strRedeemScript)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script"); } // Process. // // P2SH if (isP2SH) { // Import redeem script. std::vector vData(ParseHex(strRedeemScript)); CScript redeemScript = CScript(vData.begin(), vData.end()); // Invalid P2SH address if (!script.IsPayToScriptHash()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script"); } pwallet->MarkDirty(); if (!pwallet->AddWatchOnly(redeemScript, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } CTxDestination redeem_dest = CScriptID(redeemScript); CScript redeemDestination = GetScriptForDestination(redeem_dest); if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private " "key for this address or script"); } pwallet->MarkDirty(); if (!pwallet->AddWatchOnly(redeemDestination, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } // add to address book or update label if (IsValidDestination(dest)) { pwallet->SetAddressBook(dest, label, "receive"); } // Import private keys. if (keys.size()) { for (size_t i = 0; i < keys.size(); i++) { const std::string &privkey = keys[i].get_str(); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(privkey); if (!fGood) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); } CKey key = vchSecret.GetKey(); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); } CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); CKeyID vchAddress = pubkey.GetID(); pwallet->MarkDirty(); pwallet->SetAddressBook(vchAddress, label, "receive"); if (pwallet->HaveKey(vchAddress)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key"); } pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; if (!pwallet->AddKeyPubKey(key, pubkey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } pwallet->UpdateTimeFirstKey(timestamp); } } success = true; } else { // Import public keys. if (pubKeys.size() && keys.size() == 0) { const std::string &strPubKey = pubKeys[0].get_str(); if (!IsHex(strPubKey)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); } std::vector vData(ParseHex(strPubKey)); CPubKey pubKey(vData.begin(), vData.end()); if (!pubKey.IsFullyValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); } CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { CTxDestination destination; if (ExtractDestination(script, destination)) { if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } } } CScript pubKeyScript = GetScriptForDestination(pubkey_dest); if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " "contains the private " "key for this address " "or script"); } pwallet->MarkDirty(); if (!pwallet->AddWatchOnly(pubKeyScript, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } // add to address book or update label if (IsValidDestination(pubkey_dest)) { pwallet->SetAddressBook(pubkey_dest, label, "receive"); } // TODO Is this necessary? CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey); if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " "contains the private " "key for this address " "or script"); } pwallet->MarkDirty(); if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } success = true; } // Import private keys. if (keys.size()) { const std::string &strPrivkey = keys[0].get_str(); // Checks. CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(strPrivkey); if (!fGood) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); } CKey key = vchSecret.GetKey(); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); } CPubKey pubKey = key.GetPubKey(); assert(key.VerifyPubKey(pubKey)); CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { CTxDestination destination; if (ExtractDestination(script, destination)) { if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } } } CKeyID vchAddress = pubKey.GetID(); pwallet->MarkDirty(); pwallet->SetAddressBook(vchAddress, label, "receive"); if (pwallet->HaveKey(vchAddress)) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " "contains the private " "key for this address " "or script"); } pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; if (!pwallet->AddKeyPubKey(key, pubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } pwallet->UpdateTimeFirstKey(timestamp); success = true; } // Import scriptPubKey only. if (pubKeys.size() == 0 && keys.size() == 0) { if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " "contains the private " "key for this address " "or script"); } pwallet->MarkDirty(); if (!pwallet->AddWatchOnly(script, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } if (scriptPubKey.getType() == UniValue::VOBJ) { // add to address book or update label if (IsValidDestination(dest)) { pwallet->SetAddressBook(dest, label, "receive"); } } success = true; } } UniValue result = UniValue(UniValue::VOBJ); result.pushKV("success", UniValue(success)); return result; } catch (const UniValue &e) { UniValue result = UniValue(UniValue::VOBJ); result.pushKV("success", UniValue(false)); result.pushKV("error", e); return result; } catch (...) { UniValue result = UniValue(UniValue::VOBJ); result.pushKV("success", UniValue(false)); result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields")); return result; } } int64_t GetImportTimestamp(const UniValue &data, int64_t now) { if (data.exists("timestamp")) { const UniValue ×tamp = data["timestamp"]; if (timestamp.isNum()) { return timestamp.get_int64(); } else if (timestamp.isStr() && timestamp.get_str() == "now") { return now; } throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp " "value for key. got type %s", uvTypeName(timestamp.type()))); } throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key"); } UniValue importmulti(const Config &config, const JSONRPCRequest &mainRequest) { CWallet *const pwallet = GetWalletForJSONRPCRequest(mainRequest); if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) { return NullUniValue; } // clang-format off if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2) { throw std::runtime_error( "importmulti \"requests\" ( \"options\" )\n\n" "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n" "Arguments:\n" "1. requests (array, required) Data to be imported\n" " [ (array of json objects)\n" " {\n" " \"scriptPubKey\": \"