aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp170
-rw-r--r--src/rpc/blockchain.h12
-rw-r--r--src/rpc/mining.cpp44
-rw-r--r--src/rpc/mining.h15
-rw-r--r--src/rpc/misc.cpp14
-rw-r--r--src/rpc/net.cpp1
-rw-r--r--src/rpc/rawtransaction.cpp481
-rw-r--r--src/rpc/rawtransaction.h22
-rw-r--r--src/rpc/rawtransaction_util.cpp283
-rw-r--r--src/rpc/rawtransaction_util.h32
-rw-r--r--src/rpc/server.cpp120
-rw-r--r--src/rpc/server.h42
-rw-r--r--src/rpc/util.cpp137
-rw-r--r--src/rpc/util.h42
14 files changed, 734 insertions, 681 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 53f8dfebaa..5db3a6c987 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -7,13 +7,14 @@
#include <amount.h>
#include <base58.h>
+#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <hash.h>
+#include <index/blockfilterindex.h>
#include <index/txindex.h>
#include <key_io.h>
#include <policy/feerate.h>
@@ -29,6 +30,7 @@
#include <txmempool.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -91,6 +93,9 @@ static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* b
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex)
{
+ // Serialize passed information without accessing chain state of the active chain!
+ AssertLockNotHeld(cs_main); // For performance reasons
+
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
const CBlockIndex* pnext;
@@ -117,6 +122,9 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
{
+ // Serialize passed information without accessing chain state of the active chain!
+ AssertLockNotHeld(cs_main); // For performance reasons
+
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
const CBlockIndex* pnext;
@@ -174,7 +182,7 @@ static UniValue getblockcount(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return chainActive.Height();
+ return ::ChainActive().Height();
}
static UniValue getbestblockhash(const JSONRPCRequest& request)
@@ -194,7 +202,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return chainActive.Tip()->GetBlockHash().GetHex();
+ return ::ChainActive().Tip()->GetBlockHash().GetHex();
}
void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
@@ -373,7 +381,7 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return GetDifficulty(chainActive.Tip());
+ return GetDifficulty(::ChainActive().Tip());
}
static std::string EntryDescriptionString()
@@ -724,10 +732,10 @@ static UniValue getblockhash(const JSONRPCRequest& request)
LOCK(cs_main);
int nHeight = request.params[0].get_int();
- if (nHeight < 0 || nHeight > chainActive.Height())
+ if (nHeight < 0 || nHeight > ::ChainActive().Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
- CBlockIndex* pblockindex = chainActive[nHeight];
+ CBlockIndex* pblockindex = ::ChainActive()[nHeight];
return pblockindex->GetBlockHash().GetHex();
}
@@ -783,7 +791,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
LOCK(cs_main);
pblockindex = LookupBlockIndex(hash);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
}
if (!pblockindex) {
@@ -822,9 +830,7 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
static UniValue getblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
- RPCHelpMan{"getblock",
+ const RPCHelpMan help{"getblock",
"\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
"If verbosity is 1, returns an Object with information about block <hash>.\n"
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
@@ -876,9 +882,11 @@ static UniValue getblock(const JSONRPCRequest& request)
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- }.ToString());
+ };
- LOCK(cs_main);
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
uint256 hash(ParseHashV(request.params[0], "blockhash"));
@@ -890,12 +898,20 @@ static UniValue getblock(const JSONRPCRequest& request)
verbosity = request.params[1].get_bool() ? 1 : 0;
}
- const CBlockIndex* pblockindex = LookupBlockIndex(hash);
- if (!pblockindex) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
- }
+ CBlock block;
+ const CBlockIndex* pblockindex;
+ const CBlockIndex* tip;
+ {
+ LOCK(cs_main);
+ pblockindex = LookupBlockIndex(hash);
+ tip = ::ChainActive().Tip();
+
+ if (!pblockindex) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
- const CBlock block = GetBlockChecked(pblockindex);
+ block = GetBlockChecked(pblockindex);
+ }
if (verbosity <= 0)
{
@@ -905,7 +921,7 @@ static UniValue getblock(const JSONRPCRequest& request)
return strHex;
}
- return blockToJSON(block, chainActive.Tip(), pblockindex, verbosity >= 2);
+ return blockToJSON(block, tip, pblockindex, verbosity >= 2);
}
struct CCoinsStats
@@ -1010,7 +1026,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
// 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);
+ CBlockIndex* pindex = ::ChainActive().FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
@@ -1018,7 +1034,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
}
unsigned int height = (unsigned int) heightParam;
- unsigned int chainHeight = (unsigned int) chainActive.Height();
+ unsigned int chainHeight = (unsigned int) ::ChainActive().Height();
if (chainHeight < Params().PruneAfterHeight())
throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
else if (height > chainHeight)
@@ -1317,10 +1333,10 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
LOCK(cs_main);
- const CBlockIndex* tip = chainActive.Tip();
+ const CBlockIndex* tip = ::ChainActive().Tip();
UniValue obj(UniValue::VOBJ);
obj.pushKV("chain", Params().NetworkIDString());
- obj.pushKV("blocks", (int)chainActive.Height());
+ obj.pushKV("blocks", (int)::ChainActive().Height());
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
@@ -1417,11 +1433,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
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.
+ * 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()
+ * - add ::ChainActive().Tip()
*/
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
std::set<const CBlockIndex*> setOrphans;
@@ -1429,7 +1445,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
{
- if (!chainActive.Contains(item.second)) {
+ if (!::ChainActive().Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
}
@@ -1443,7 +1459,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
}
// Always report the currently active tip.
- setTips.insert(chainActive.Tip());
+ setTips.insert(::ChainActive().Tip());
/* Construct the output array. */
UniValue res(UniValue::VARR);
@@ -1453,11 +1469,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
obj.pushKV("height", block->nHeight);
obj.pushKV("hash", block->phashBlock->GetHex());
- const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight;
+ const int branchLen = block->nHeight - ::ChainActive().FindFork(block)->nHeight;
obj.pushKV("branchlen", branchLen);
std::string status;
- if (chainActive.Contains(block)) {
+ if (::ChainActive().Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {
@@ -1486,7 +1502,10 @@ static UniValue getchaintips(const JSONRPCRequest& request)
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
{
+ // Make sure this call is atomic in the pool.
+ LOCK(pool.cs);
UniValue ret(UniValue::VOBJ);
+ ret.pushKV("loaded", pool.IsLoaded());
ret.pushKV("size", (int64_t)pool.size());
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
@@ -1507,6 +1526,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
{},
RPCResult{
"{\n"
+ " \"loaded\": true|false (boolean) True if the mempool is fully loaded\n"
" \"size\": xxxxx, (numeric) Current tx count\n"
" \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n"
" \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n"
@@ -1674,7 +1694,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
if (request.params[1].isNull()) {
LOCK(cs_main);
- pindex = chainActive.Tip();
+ pindex = ::ChainActive().Tip();
} else {
uint256 hash(ParseHashV(request.params[1], "blockhash"));
LOCK(cs_main);
@@ -1682,7 +1702,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- if (!chainActive.Contains(pindex)) {
+ if (!::ChainActive().Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
}
}
@@ -1843,7 +1863,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
CBlockIndex* pindex;
if (request.params[0].isNum()) {
const int height = request.params[0].get_int();
- const int current_tip = chainActive.Height();
+ const int current_tip = ::ChainActive().Height();
if (height < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
}
@@ -1851,14 +1871,14 @@ static UniValue getblockstats(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
}
- pindex = chainActive[height];
+ pindex = ::ChainActive()[height];
} else {
const uint256 hash(ParseHashV(request.params[0], "hash_or_height"));
pindex = LookupBlockIndex(hash);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- if (!chainActive.Contains(pindex)) {
+ if (!::ChainActive().Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Block is not in chain %s", Params().NetworkIDString()));
}
}
@@ -2057,11 +2077,11 @@ static UniValue savemempool(const JSONRPCRequest& request)
}.ToString());
}
- if (!g_is_mempool_loaded) {
+ if (!::mempool.IsLoaded()) {
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
}
- if (!DumpMempool()) {
+ if (!DumpMempool(::mempool)) {
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
}
@@ -2295,6 +2315,85 @@ UniValue scantxoutset(const JSONRPCRequest& request)
return result;
}
+static UniValue getblockfilter(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw std::runtime_error(
+ RPCHelpMan{"getblockfilter",
+ "\nRetrieve a BIP 157 content filter for a particular block.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"},
+ {"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"},
+ },
+ RPCResult{
+ "{\n"
+ " \"filter\" : (string) the hex-encoded filter data\n"
+ " \"header\" : (string) the hex-encoded filter header\n"
+ "}\n"
+ },
+ RPCExamples{
+ HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")
+ }
+ }.ToString()
+ );
+ }
+
+ uint256 block_hash = ParseHashV(request.params[0], "blockhash");
+ std::string filtertype_name = "basic";
+ if (!request.params[1].isNull()) {
+ filtertype_name = request.params[1].get_str();
+ }
+
+ BlockFilterType filtertype;
+ if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
+ }
+
+ BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
+ if (!index) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
+ }
+
+ const CBlockIndex* block_index;
+ bool block_was_connected;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block_hash);
+ if (!block_index) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
+ }
+
+ bool index_ready = index->BlockUntilSyncedToCurrentChain();
+
+ BlockFilter filter;
+ uint256 filter_header;
+ if (!index->LookupFilter(block_index, filter) ||
+ !index->LookupFilterHeader(block_index, filter_header)) {
+ int err_code;
+ std::string errmsg = "Filter not found.";
+
+ if (!block_was_connected) {
+ err_code = RPC_INVALID_ADDRESS_OR_KEY;
+ errmsg += " Block was not connected to active chain.";
+ } else if (!index_ready) {
+ err_code = RPC_MISC_ERROR;
+ errmsg += " Block filters are still in the process of being indexed.";
+ } else {
+ err_code = RPC_INTERNAL_ERROR;
+ errmsg += " This error is unexpected and indicates index corruption.";
+ }
+
+ throw JSONRPCError(err_code, errmsg);
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
+ ret.pushKV("header", filter_header.GetHex());
+ return ret;
+}
+
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -2322,6 +2421,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
{ "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
+ { "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} },
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 55d1de453f..ff461fbcbc 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -5,9 +5,13 @@
#ifndef BITCOIN_RPC_BLOCKCHAIN_H
#define BITCOIN_RPC_BLOCKCHAIN_H
-#include <vector>
-#include <stdint.h>
#include <amount.h>
+#include <sync.h>
+
+#include <stdint.h>
+#include <vector>
+
+extern RecursiveMutex cs_main;
class CBlock;
class CBlockIndex;
@@ -28,7 +32,7 @@ double GetDifficulty(const CBlockIndex* blockindex);
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
/** Block description to JSON */
-UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false);
+UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false) LOCKS_EXCLUDED(cs_main);
/** Mempool information to JSON */
UniValue MempoolInfoToJSON(const CTxMemPool& pool);
@@ -37,7 +41,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool);
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false);
/** Block header to JSON */
-UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex);
+UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex) LOCKS_EXCLUDED(cs_main);
/** Used by getblockstats to get feerates at different percentiles by weight */
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 480d610235..1831562100 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -16,13 +16,16 @@
#include <policy/fees.h>
#include <pow.h>
#include <rpc/blockchain.h>
-#include <rpc/mining.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/script.h>
#include <shutdown.h>
#include <txmempool.h>
+#include <univalue.h>
+#include <util/fees.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -37,10 +40,10 @@
* 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();
+ CBlockIndex *pb = ::ChainActive().Tip();
- if (height >= 0 && height < chainActive.Height())
- pb = chainActive[height];
+ if (height >= 0 && height < ::ChainActive().Height())
+ pb = ::ChainActive()[height];
if (pb == nullptr || !pb->nHeight)
return 0;
@@ -98,7 +101,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
}
-UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
+static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
{
static const int nInnerLoopCount = 0x10000;
int nHeightEnd = 0;
@@ -106,20 +109,20 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
{ // Don't keep cs_main locked
LOCK(cs_main);
- nHeight = chainActive.Height();
+ nHeight = ::ChainActive().Height();
nHeightEnd = nHeight+nGenerate;
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd && !ShutdownRequested())
{
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
- IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
+ IncrementExtraNonce(pblock, ::ChainActive().Tip(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
++pblock->nNonce;
@@ -136,12 +139,6 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
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;
}
@@ -179,10 +176,9 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
}
- std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
- coinbaseScript->reserveScript = GetScriptForDestination(destination);
+ CScript coinbase_script = GetScriptForDestination(destination);
- return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
+ return generateBlocks(coinbase_script, nGenerate, nMaxTries);
}
static UniValue getmininginfo(const JSONRPCRequest& request)
@@ -214,10 +210,10 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
- obj.pushKV("blocks", (int)chainActive.Height());
+ obj.pushKV("blocks", (int)::ChainActive().Height());
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
- obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
+ obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip()));
obj.pushKV("networkhashps", getnetworkhashps(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
@@ -413,7 +409,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
return "duplicate-inconclusive";
}
- CBlockIndex* const pindexPrev = chainActive.Tip();
+ CBlockIndex* const pindexPrev = ::ChainActive().Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
@@ -469,7 +465,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
else
{
// NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
- hashWatchedChain = chainActive.Tip()->GetBlockHash();
+ hashWatchedChain = ::ChainActive().Tip()->GetBlockHash();
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
@@ -507,7 +503,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
static CBlockIndex* pindexPrev;
static int64_t nStart;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
- if (pindexPrev != chainActive.Tip() ||
+ if (pindexPrev != ::ChainActive().Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
@@ -515,7 +511,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
- CBlockIndex* pindexPrevNew = chainActive.Tip();
+ CBlockIndex* pindexPrevNew = ::ChainActive().Tip();
nStart = GetTime();
// Create new block
@@ -650,7 +646,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
result.pushKV("transactions", transactions);
result.pushKV("coinbaseaux", aux);
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
- result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast));
+ 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);
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
deleted file mode 100644
index be9a973315..0000000000
--- a/src/rpc/mining.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2017 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_RPC_MINING_H
-#define BITCOIN_RPC_MINING_H
-
-#include <script/script.h>
-
-#include <univalue.h>
-
-/** Generate blocks (mine) */
-UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript);
-
-#endif
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index e07b02aa23..7008a83143 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -20,6 +20,7 @@
#include <timedata.h>
#include <util/system.h>
#include <util/strencodings.h>
+#include <util/validation.h>
#include <warnings.h>
#include <stdint.h>
@@ -218,8 +219,8 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]);
}
- FlatSigningProvider provider;
- auto desc = Parse(desc_str, provider, /* require_checksum = */ true);
+ FlatSigningProvider key_provider;
+ auto desc = Parse(desc_str, key_provider, /* require_checksum = */ true);
if (!desc) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
}
@@ -235,8 +236,9 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
UniValue addresses(UniValue::VARR);
for (int i = range_begin; i <= range_end; ++i) {
+ FlatSigningProvider provider;
std::vector<CScript> scripts;
- if (!desc->Expand(i, provider, scripts, provider)) {
+ if (!desc->Expand(i, key_provider, scripts, provider)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
}
@@ -295,8 +297,8 @@ static UniValue verifymessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
- const CKeyID *keyID = boost::get<CKeyID>(&destination);
- if (!keyID) {
+ const PKHash *pkhash = boost::get<PKHash>(&destination);
+ if (!pkhash) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
@@ -314,7 +316,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
return false;
- return (pubkey.GetID() == *keyID);
+ return (pubkey.GetID() == *pkhash);
}
static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index c7b3478f44..e8cdce623c 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -12,6 +12,7 @@
#include <net_processing.h>
#include <netbase.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <rpc/protocol.h>
#include <rpc/util.h>
#include <sync.h>
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 5dcd04323e..b3926786db 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -15,12 +15,15 @@
#include <key_io.h>
#include <keystore.h>
#include <merkleblock.h>
+#include <node/coin.h>
+#include <node/psbt.h>
#include <node/transaction.h>
#include <policy/policy.h>
#include <policy/rbf.h>
+#include <policy/settings.h>
#include <primitives/transaction.h>
#include <psbt.h>
-#include <rpc/rawtransaction.h>
+#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/script.h>
@@ -61,8 +64,8 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
entry.pushKV("blockhash", hashBlock.GetHex());
CBlockIndex* pindex = LookupBlockIndex(hashBlock);
if (pindex) {
- if (chainActive.Contains(pindex)) {
- entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight);
+ if (::ChainActive().Contains(pindex)) {
+ entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight);
entry.pushKV("time", pindex->GetBlockTime());
entry.pushKV("blocktime", pindex->GetBlockTime());
}
@@ -181,7 +184,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
if (!blockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
- in_active_chain = chainActive.Contains(blockindex);
+ in_active_chain = ::ChainActive().Contains(blockindex);
}
bool f_txindex_ready = false;
@@ -271,7 +274,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
for (const auto& tx : setTxids) {
const Coin& coin = AccessByTxid(*pcoinsTip, tx);
if (!coin.IsSpent()) {
- pblockindex = chainActive[coin.nHeight];
+ pblockindex = ::ChainActive()[coin.nHeight];
break;
}
}
@@ -345,7 +348,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
- if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) {
+ if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
@@ -359,119 +362,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
return res;
}
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
-{
- if (inputs_in.isNull() || outputs_in.isNull())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
-
- UniValue inputs = inputs_in.get_array();
- const bool outputs_is_obj = outputs_in.isObject();
- UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
-
- CMutableTransaction rawTx;
-
- if (!locktime.isNull()) {
- int64_t nLockTime = locktime.get_int64();
- if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
- rawTx.nLockTime = nLockTime;
- }
-
- bool rbfOptIn = rbf.isTrue();
-
- for (unsigned int idx = 0; idx < inputs.size(); idx++) {
- const UniValue& input = inputs[idx];
- const UniValue& o = input.get_obj();
-
- uint256 txid = ParseHashO(o, "txid");
-
- const UniValue& vout_v = find_value(o, "vout");
- if (!vout_v.isNum())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
- int nOutput = vout_v.get_int();
- if (nOutput < 0)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
-
- uint32_t nSequence;
- if (rbfOptIn) {
- nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
- } else if (rawTx.nLockTime) {
- nSequence = CTxIn::SEQUENCE_FINAL - 1;
- } else {
- nSequence = CTxIn::SEQUENCE_FINAL;
- }
-
- // 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 > CTxIn::SEQUENCE_FINAL) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
- } else {
- nSequence = (uint32_t)seqNr64;
- }
- }
-
- CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
-
- rawTx.vin.push_back(in);
- }
-
- if (!outputs_is_obj) {
- // Translate array of key-value pairs into dict
- UniValue outputs_dict = UniValue(UniValue::VOBJ);
- for (size_t i = 0; i < outputs.size(); ++i) {
- const UniValue& output = outputs[i];
- if (!output.isObject()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
- }
- if (output.size() != 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
- }
- outputs_dict.pushKVs(output);
- }
- outputs = std::move(outputs_dict);
- }
-
- // Duplicate checking
- std::set<CTxDestination> destinations;
- bool has_data{false};
-
- for (const std::string& name_ : outputs.getKeys()) {
- if (name_ == "data") {
- if (has_data) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
- }
- has_data = true;
- std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
-
- CTxOut out(0, CScript() << OP_RETURN << data);
- rawTx.vout.push_back(out);
- } else {
- CTxDestination destination = DecodeDestination(name_);
- 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);
- CAmount nAmount = AmountFromValue(outputs[name_]);
-
- CTxOut out(nAmount, scriptPubKey);
- rawTx.vout.push_back(out);
- }
- }
-
- if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
- }
-
- return rawTx;
-}
-
static UniValue createrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
@@ -682,7 +572,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
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)));
+ r.pushKV("p2sh", EncodeDestination(ScriptHash(script)));
// P2SH and witness programs cannot be wrapped in P2WSH, if this script
// is a witness program, don't return addresses for a segwit programs.
if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") {
@@ -709,7 +599,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
ScriptPubKeyToUniv(segwitScr, sr, /* fIncludeHex */ true);
- sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
+ sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(segwitScr)));
r.pushKV("segwit", sr);
}
}
@@ -717,23 +607,6 @@ static UniValue decodescript(const JSONRPCRequest& request)
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.hash.ToString());
- entry.pushKV("vout", (uint64_t)txin.prevout.n);
- UniValue witness(UniValue::VARR);
- for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
- witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
- }
- entry.pushKV("witness", witness);
- 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 JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
@@ -818,152 +691,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(CTransaction(mergedTx));
}
-// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237):
-// This function is called from both wallet and node rpcs
-// (signrawtransactionwithwallet and signrawtransactionwithkey). It should be
-// moved to a util file so wallet code doesn't need to link against node code.
-// Also the dependency on interfaces::Chain should be removed, so
-// signrawtransactionwithkey doesn't need access to a Chain instance.
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
-{
- // Fetch previous transactions (inputs):
- std::map<COutPoint, Coin> coins;
- for (const CTxIn& txin : mtx.vin) {
- coins[txin.prevout]; // Create empty map entry keyed by prevout.
- }
- chain.findCoins(coins);
-
- // Add previous txouts given in the RPC call:
- if (!prevTxsUnival.isNull()) {
- UniValue prevTxs = prevTxsUnival.get_array();
- for (unsigned int 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)},
- });
-
- 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<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
- CScript scriptPubKey(pkData.begin(), pkData.end());
-
- {
- auto coin = coins.find(out);
- if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
- std::string err("Previous output scriptPubKey mismatch:\n");
- err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
- ScriptToAsmStr(scriptPubKey);
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
- }
- Coin newcoin;
- newcoin.out.scriptPubKey = scriptPubKey;
- newcoin.out.nValue = MAX_MONEY;
- if (prevOut.exists("amount")) {
- newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
- }
- newcoin.nHeight = 1;
- coins[out] = std::move(newcoin);
- }
-
- // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
- if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
- RPCTypeCheckObj(prevOut,
- {
- {"redeemScript", UniValueType(UniValue::VSTR)},
- {"witnessScript", UniValueType(UniValue::VSTR)},
- }, true);
- UniValue rs = find_value(prevOut, "redeemScript");
- if (!rs.isNull()) {
- std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
- CScript redeemScript(rsData.begin(), rsData.end());
- keystore->AddCScript(redeemScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
- keystore->AddCScript(GetScriptForWitness(redeemScript));
- }
- UniValue ws = find_value(prevOut, "witnessScript");
- if (!ws.isNull()) {
- std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
- CScript witnessScript(wsData.begin(), wsData.end());
- keystore->AddCScript(witnessScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- keystore->AddCScript(GetScriptForWitness(witnessScript));
- }
- }
- }
- }
-
- int nHashType = ParseSighashString(hashType);
-
- bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
-
- // 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 (unsigned int i = 0; i < mtx.vin.size(); i++) {
- CTxIn& txin = mtx.vin[i];
- auto coin = coins.find(txin.prevout);
- if (coin == coins.end() || coin->second.IsSpent()) {
- TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
- continue;
- }
- const CScript& prevPubKey = coin->second.out.scriptPubKey;
- const CAmount& amount = coin->second.out.nValue;
-
- SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
- // Only sign SIGHASH_SINGLE if there's a corresponding output:
- if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
- }
-
- UpdateInput(txin, sigdata);
-
- // amount must be specified for valid segwit signature
- if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
- }
-
- ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, 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 JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
@@ -1044,7 +771,14 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
keystore.AddKey(key);
}
- return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]);
+ // Fetch previous transactions (inputs):
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ FindCoins(coins);
+
+ return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]);
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
@@ -1054,7 +788,9 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
- {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
+ {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE),
+ "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
+ "/kB.\nSet to 0 to accept any fee rate.\n"},
},
RPCResult{
"\"hex\" (string) The transaction hash in hex\n"
@@ -1090,15 +826,13 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
// TODO: temporary migration code for old clients. Remove in v0.20
if (request.params[1].isBool()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
- } else if (request.params[1].isNum()) {
+ } else if (!request.params[1].isNull()) {
size_t weight = GetTransactionWeight(*tx);
CFeeRate fr(AmountFromValue(request.params[1]));
// the +3/4 part rounds the value up, and is the same formula used when
// calculating the fee for a transaction
// (see GetVirtualTransactionSize)
max_raw_tx_fee = fr.GetFee((weight+3)/4);
- } else if (!request.params[1].isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
}
uint256 txid;
@@ -1172,15 +906,13 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
// TODO: temporary migration code for old clients. Remove in v0.20
if (request.params[1].isBool()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
- } else if (request.params[1].isNum()) {
+ } else if (!request.params[1].isNull()) {
size_t weight = GetTransactionWeight(*tx);
CFeeRate fr(AmountFromValue(request.params[1]));
// the +3/4 part rounds the value up, and is the same formula used when
// calculating the fee for a transaction
// (see GetVirtualTransactionSize)
max_raw_tx_fee = fr.GetFee((weight+3)/4);
- } else if (!request.params[1].isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
}
UniValue result(UniValue::VARR);
@@ -1549,7 +1281,6 @@ UniValue combinepsbt(const JSONRPCRequest& request)
throw JSONRPCTransactionError(error);
}
- UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
@@ -1944,148 +1675,56 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
- // Go through each input and build status
+ PSBTAnalysis psbta = AnalyzePSBT(psbtx);
+
UniValue result(UniValue::VOBJ);
UniValue inputs_result(UniValue::VARR);
- bool calc_fee = true;
- bool all_final = true;
- bool only_missing_sigs = true;
- bool only_missing_final = false;
- CAmount in_amt = 0;
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- PSBTInput& input = psbtx.inputs[i];
+ for (const auto& input : psbta.inputs) {
UniValue input_univ(UniValue::VOBJ);
UniValue missing(UniValue::VOBJ);
- // Check for a UTXO
- CTxOut utxo;
- if (psbtx.GetInputUTXO(utxo, i)) {
- in_amt += utxo.nValue;
- input_univ.pushKV("has_utxo", true);
- } else {
- input_univ.pushKV("has_utxo", false);
- input_univ.pushKV("is_final", false);
- input_univ.pushKV("next", "updater");
- calc_fee = false;
- }
+ input_univ.pushKV("has_utxo", input.has_utxo);
+ input_univ.pushKV("is_final", input.is_final);
+ input_univ.pushKV("next", PSBTRoleName(input.next));
- // Check if it is final
- if (!utxo.IsNull() && !PSBTInputSigned(input)) {
- input_univ.pushKV("is_final", false);
- all_final = false;
-
- // Figure out what is missing
- SignatureData outdata;
- bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
-
- // Things are missing
- if (!complete) {
- if (!outdata.missing_pubkeys.empty()) {
- // Missing pubkeys
- UniValue missing_pubkeys_univ(UniValue::VARR);
- for (const CKeyID& pubkey : outdata.missing_pubkeys) {
- missing_pubkeys_univ.push_back(HexStr(pubkey));
- }
- missing.pushKV("pubkeys", missing_pubkeys_univ);
- }
- if (!outdata.missing_redeem_script.IsNull()) {
- // Missing redeemScript
- missing.pushKV("redeemscript", HexStr(outdata.missing_redeem_script));
- }
- if (!outdata.missing_witness_script.IsNull()) {
- // Missing witnessScript
- missing.pushKV("witnessscript", HexStr(outdata.missing_witness_script));
- }
- if (!outdata.missing_sigs.empty()) {
- // Missing sigs
- UniValue missing_sigs_univ(UniValue::VARR);
- for (const CKeyID& pubkey : outdata.missing_sigs) {
- missing_sigs_univ.push_back(HexStr(pubkey));
- }
- missing.pushKV("signatures", missing_sigs_univ);
- }
- input_univ.pushKV("missing", missing);
-
- // If we are only missing signatures and nothing else, then next is signer
- if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
- input_univ.pushKV("next", "signer");
- } else {
- only_missing_sigs = false;
- input_univ.pushKV("next", "updater");
- }
- } else {
- only_missing_final = true;
- input_univ.pushKV("next", "finalizer");
+ if (!input.missing_pubkeys.empty()) {
+ UniValue missing_pubkeys_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : input.missing_pubkeys) {
+ missing_pubkeys_univ.push_back(HexStr(pubkey));
}
- } else if (!utxo.IsNull()){
- input_univ.pushKV("is_final", true);
+ missing.pushKV("pubkeys", missing_pubkeys_univ);
+ }
+ if (!input.missing_redeem_script.IsNull()) {
+ missing.pushKV("redeemscript", HexStr(input.missing_redeem_script));
+ }
+ if (!input.missing_witness_script.IsNull()) {
+ missing.pushKV("witnessscript", HexStr(input.missing_witness_script));
+ }
+ if (!input.missing_sigs.empty()) {
+ UniValue missing_sigs_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : input.missing_sigs) {
+ missing_sigs_univ.push_back(HexStr(pubkey));
+ }
+ missing.pushKV("signatures", missing_sigs_univ);
+ }
+ if (!missing.getKeys().empty()) {
+ input_univ.pushKV("missing", missing);
}
inputs_result.push_back(input_univ);
}
result.pushKV("inputs", inputs_result);
- if (all_final) {
- only_missing_sigs = false;
- result.pushKV("next", "extractor");
+ if (psbta.estimated_vsize != nullopt) {
+ result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
}
- if (calc_fee) {
- // Get the output amount
- CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0),
- [](CAmount a, const CTxOut& b) {
- return a += b.nValue;
- }
- );
-
- // Get the fee
- CAmount fee = in_amt - out_amt;
-
- // Estimate the size
- CMutableTransaction mtx(*psbtx.tx);
- CCoinsView view_dummy;
- CCoinsViewCache view(&view_dummy);
- bool success = true;
-
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- PSBTInput& input = psbtx.inputs[i];
- if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true)) {
- mtx.vin[i].scriptSig = input.final_script_sig;
- mtx.vin[i].scriptWitness = input.final_script_witness;
-
- Coin newcoin;
- if (!psbtx.GetInputUTXO(newcoin.out, i)) {
- success = false;
- break;
- }
- newcoin.nHeight = 1;
- view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
- } else {
- success = false;
- break;
- }
- }
-
- if (success) {
- CTransaction ctx = CTransaction(mtx);
- size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
- result.pushKV("estimated_vsize", (int)size);
- // Estimate fee rate
- CFeeRate feerate(fee, size);
- result.pushKV("estimated_feerate", ValueFromAmount(feerate.GetFeePerK()));
- }
- result.pushKV("fee", ValueFromAmount(fee));
-
- if (only_missing_sigs) {
- result.pushKV("next", "signer");
- } else if (only_missing_final) {
- result.pushKV("next", "finalizer");
- } else if (all_final) {
- result.pushKV("next", "extractor");
- } else {
- result.pushKV("next", "updater");
- }
- } else {
- result.pushKV("next", "updater");
+ if (psbta.estimated_feerate != nullopt) {
+ result.pushKV("estimated_feerate", ValueFromAmount(psbta.estimated_feerate->GetFeePerK()));
}
+ if (psbta.fee != nullopt) {
+ result.pushKV("fee", ValueFromAmount(*psbta.fee));
+ }
+ result.pushKV("next", PSBTRoleName(psbta.next));
+
return result;
}
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
deleted file mode 100644
index 52d701d1c3..0000000000
--- a/src/rpc/rawtransaction.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_RPC_RAWTRANSACTION_H
-#define BITCOIN_RPC_RAWTRANSACTION_H
-
-class CBasicKeyStore;
-struct CMutableTransaction;
-class UniValue;
-
-namespace interfaces {
-class Chain;
-} // namespace interfaces
-
-/** Sign a transaction with the given keystore and previous transactions */
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
-
-/** Create a transaction from univalue parameters */
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
-
-#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
new file mode 100644
index 0000000000..8af491977c
--- /dev/null
+++ b/src/rpc/rawtransaction_util.cpp
@@ -0,0 +1,283 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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_util.h>
+
+#include <coins.h>
+#include <core_io.h>
+#include <interfaces/chain.h>
+#include <key_io.h>
+#include <keystore.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <rpc/protocol.h>
+#include <rpc/util.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/rbf.h>
+#include <util/strencodings.h>
+
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
+{
+ if (inputs_in.isNull() || outputs_in.isNull())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
+
+ UniValue inputs = inputs_in.get_array();
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
+
+ CMutableTransaction rawTx;
+
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.get_int64();
+ if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
+ rawTx.nLockTime = nLockTime;
+ }
+
+ bool rbfOptIn = rbf.isTrue();
+
+ for (unsigned int idx = 0; idx < inputs.size(); idx++) {
+ const UniValue& input = inputs[idx];
+ const UniValue& o = input.get_obj();
+
+ uint256 txid = ParseHashO(o, "txid");
+
+ const UniValue& vout_v = find_value(o, "vout");
+ if (!vout_v.isNum())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
+ int nOutput = vout_v.get_int();
+ if (nOutput < 0)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
+
+ uint32_t nSequence;
+ if (rbfOptIn) {
+ nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
+ } else if (rawTx.nLockTime) {
+ nSequence = CTxIn::SEQUENCE_FINAL - 1;
+ } else {
+ nSequence = CTxIn::SEQUENCE_FINAL;
+ }
+
+ // 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 > CTxIn::SEQUENCE_FINAL) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
+ } else {
+ nSequence = (uint32_t)seqNr64;
+ }
+ }
+
+ CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
+
+ rawTx.vin.push_back(in);
+ }
+
+ if (!outputs_is_obj) {
+ // Translate array of key-value pairs into dict
+ UniValue outputs_dict = UniValue(UniValue::VOBJ);
+ for (size_t i = 0; i < outputs.size(); ++i) {
+ const UniValue& output = outputs[i];
+ if (!output.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
+ }
+ if (output.size() != 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
+ }
+ outputs_dict.pushKVs(output);
+ }
+ outputs = std::move(outputs_dict);
+ }
+
+ // Duplicate checking
+ std::set<CTxDestination> destinations;
+ bool has_data{false};
+
+ for (const std::string& name_ : outputs.getKeys()) {
+ if (name_ == "data") {
+ if (has_data) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
+ }
+ has_data = true;
+ std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
+
+ CTxOut out(0, CScript() << OP_RETURN << data);
+ rawTx.vout.push_back(out);
+ } else {
+ CTxDestination destination = DecodeDestination(name_);
+ 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);
+ CAmount nAmount = AmountFromValue(outputs[name_]);
+
+ CTxOut out(nAmount, scriptPubKey);
+ rawTx.vout.push_back(out);
+ }
+ }
+
+ if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
+ }
+
+ return rawTx;
+}
+
+/** 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.hash.ToString());
+ entry.pushKV("vout", (uint64_t)txin.prevout.n);
+ UniValue witness(UniValue::VARR);
+ for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
+ witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
+ }
+ entry.pushKV("witness", witness);
+ 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);
+}
+
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
+{
+ // Add previous txouts given in the RPC call:
+ if (!prevTxsUnival.isNull()) {
+ UniValue prevTxs = prevTxsUnival.get_array();
+ for (unsigned int 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)},
+ });
+
+ 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<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
+ CScript scriptPubKey(pkData.begin(), pkData.end());
+
+ {
+ auto coin = coins.find(out);
+ if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
+ std::string err("Previous output scriptPubKey mismatch:\n");
+ err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
+ ScriptToAsmStr(scriptPubKey);
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
+ }
+ Coin newcoin;
+ newcoin.out.scriptPubKey = scriptPubKey;
+ newcoin.out.nValue = MAX_MONEY;
+ if (prevOut.exists("amount")) {
+ newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
+ }
+ newcoin.nHeight = 1;
+ coins[out] = std::move(newcoin);
+ }
+
+ // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
+ if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
+ RPCTypeCheckObj(prevOut,
+ {
+ {"redeemScript", UniValueType(UniValue::VSTR)},
+ {"witnessScript", UniValueType(UniValue::VSTR)},
+ }, true);
+ UniValue rs = find_value(prevOut, "redeemScript");
+ if (!rs.isNull()) {
+ std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
+ CScript redeemScript(rsData.begin(), rsData.end());
+ keystore->AddCScript(redeemScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
+ keystore->AddCScript(GetScriptForWitness(redeemScript));
+ }
+ UniValue ws = find_value(prevOut, "witnessScript");
+ if (!ws.isNull()) {
+ std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
+ CScript witnessScript(wsData.begin(), wsData.end());
+ keystore->AddCScript(witnessScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ keystore->AddCScript(GetScriptForWitness(witnessScript));
+ }
+ }
+ }
+ }
+
+ int nHashType = ParseSighashString(hashType);
+
+ bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
+
+ // 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 (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
+ continue;
+ }
+ const CScript& prevPubKey = coin->second.out.scriptPubKey;
+ const CAmount& amount = coin->second.out.nValue;
+
+ SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
+ // Only sign SIGHASH_SINGLE if there's a corresponding output:
+ if (!fHashSingle || (i < mtx.vout.size())) {
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
+ }
+
+ UpdateInput(txin, sigdata);
+
+ // amount must be specified for valid segwit signature
+ if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
+ }
+
+ ScriptError serror = SCRIPT_ERR_OK;
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, 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;
+}
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
new file mode 100644
index 0000000000..c115d33a77
--- /dev/null
+++ b/src/rpc/rawtransaction_util.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_RPC_RAWTRANSACTION_UTIL_H
+#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
+
+#include <map>
+
+class CBasicKeyStore;
+class UniValue;
+struct CMutableTransaction;
+class Coin;
+class COutPoint;
+
+/**
+ * Sign a transaction with the given keystore and previous transactions
+ *
+ * @param mtx The transaction to-be-signed
+ * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
+ * @param keystore Temporary keystore containing signing keys
+ * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
+ * @param tempKeystore Whether to use temporary keystore
+ * @param hashType The signature hash type
+ * @returns JSON object with details of signed transaction
+ */
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
+
+/** Create a transaction from univalue parameters */
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+
+#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index e803fabcc6..9df4070cbb 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -77,99 +77,6 @@ void RPCServer::OnStopped(std::function<void ()> slot)
g_rpcSignals.Stopped.connect(slot);
}
-void RPCTypeCheck(const UniValue& params,
- const std::list<UniValueType>& typesExpected,
- bool fAllowNull)
-{
- unsigned int i = 0;
- for (const UniValueType& t : typesExpected) {
- if (params.size() <= i)
- break;
-
- const UniValue& v = params[i];
- if (!(fAllowNull && v.isNull())) {
- RPCTypeCheckArgument(v, t);
- }
- i++;
- }
-}
-
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
-{
- if (!typeExpected.typeAny && value.type() != typeExpected.type) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
- }
-}
-
-void RPCTypeCheckObj(const UniValue& o,
- const std::map<std::string, UniValueType>& typesExpected,
- bool fAllowNull,
- bool fStrict)
-{
- for (const auto& t : typesExpected) {
- const UniValue& v = find_value(o, t.first);
- if (!fAllowNull && v.isNull())
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
-
- if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
- std::string err = strprintf("Expected type %s for %s, got %s",
- uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
- throw JSONRPCError(RPC_TYPE_ERROR, err);
- }
- }
-
- if (fStrict)
- {
- for (const std::string& k : o.getKeys())
- {
- if (typesExpected.count(k) == 0)
- {
- std::string err = strprintf("Unexpected key %s", k);
- throw JSONRPCError(RPC_TYPE_ERROR, err);
- }
- }
- }
-}
-
-CAmount AmountFromValue(const UniValue& value)
-{
- if (!value.isNum() && !value.isStr())
- throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
- CAmount amount;
- if (!ParseFixedPoint(value.getValStr(), 8, &amount))
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
- if (!MoneyRange(amount))
- throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
- return amount;
-}
-
-uint256 ParseHashV(const UniValue& v, std::string strName)
-{
- std::string strHex(v.get_str());
- if (64 != strHex.length())
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
- if (!IsHex(strHex)) // Note: IsHex("") is false
- throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
- return uint256S(strHex);
-}
-uint256 ParseHashO(const UniValue& o, std::string strKey)
-{
- return ParseHashV(find_value(o, strKey), strKey);
-}
-std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
-{
- std::string strHex;
- if (v.isStr())
- strHex = v.get_str();
- if (!IsHex(strHex))
- throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
- return ParseHex(strHex);
-}
-std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
-{
- return ParseHexV(find_value(o, strKey), strKey);
-}
-
std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
{
std::string strRet;
@@ -297,8 +204,20 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
- RPCResults{},
- RPCExamples{""},
+ RPCResult{
+ "{\n"
+ " \"active_commands\" (array) All active commands\n"
+ " [\n"
+ " { (object) Information about an active command\n"
+ " \"method\" (string) The name of the RPC command \n"
+ " \"duration\" (numeric) The running time in microseconds\n"
+ " },...\n"
+ " ]\n"
+ "}\n"
+ },
+ RPCExamples{
+ HelpExampleCli("getrpcinfo", "")
+ + HelpExampleRpc("getrpcinfo", "")},
}.ToString()
);
}
@@ -581,17 +500,6 @@ std::vector<std::string> CRPCTable::listCommands() const
return commandList;
}
-std::string HelpExampleCli(const std::string& methodname, const std::string& args)
-{
- return "> bitcoin-cli " + methodname + " " + args + "\n";
-}
-
-std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
-{
- return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
- "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
-}
-
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
{
if (!timerInterface)
diff --git a/src/rpc/server.h b/src/rpc/server.h
index e2a85887ba..431ff0bb7c 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -27,15 +27,6 @@ namespace RPCServer
void OnStopped(std::function<void ()> slot);
}
-/** Wrapper for UniValue::VType, which includes typeAny:
- * Used to denote don't care type. */
-struct UniValueType {
- UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
- UniValueType() : typeAny(true) {}
- bool typeAny;
- UniValue::VType type;
-};
-
class JSONRPCRequest
{
public:
@@ -65,26 +56,6 @@ void SetRPCWarmupFinished();
/* returns the current warmup state. */
bool RPCIsInWarmup(std::string *outStatus);
-/**
- * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
- * the right number of arguments are passed, just that any passed are the correct type.
- */
-void RPCTypeCheck(const UniValue& params,
- const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
-
-/**
- * Type-check one argument; throws JSONRPCError if wrong type given.
- */
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
-
-/*
- Check for expected keys/value types in an Object.
-*/
-void RPCTypeCheckObj(const UniValue& o,
- const std::map<std::string, UniValueType>& typesExpected,
- bool fAllowNull = false,
- bool fStrict = false);
-
/** Opaque base class for timers returned by NewTimerFunc.
* This provides no methods at the moment, but makes sure that delete
* cleans up the whole state.
@@ -204,19 +175,6 @@ bool IsDeprecatedRPCEnabled(const std::string& method);
extern CRPCTable tableRPC;
-/**
- * Utilities: convert hex-encoded Values
- * (throws error if not hex).
- */
-extern uint256 ParseHashV(const UniValue& v, std::string strName);
-extern uint256 ParseHashO(const UniValue& o, std::string strKey);
-extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
-extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
-
-extern CAmount AmountFromValue(const UniValue& value);
-extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
-extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
-
void StartRPC();
void InterruptRPC();
void StopRPC();
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 8f8488bc5f..9cdb22001f 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -12,6 +12,110 @@
InitInterfaces* g_rpc_interfaces = nullptr;
+void RPCTypeCheck(const UniValue& params,
+ const std::list<UniValueType>& typesExpected,
+ bool fAllowNull)
+{
+ unsigned int i = 0;
+ for (const UniValueType& t : typesExpected) {
+ if (params.size() <= i)
+ break;
+
+ const UniValue& v = params[i];
+ if (!(fAllowNull && v.isNull())) {
+ RPCTypeCheckArgument(v, t);
+ }
+ i++;
+ }
+}
+
+void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
+{
+ if (!typeExpected.typeAny && value.type() != typeExpected.type) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
+ }
+}
+
+void RPCTypeCheckObj(const UniValue& o,
+ const std::map<std::string, UniValueType>& typesExpected,
+ bool fAllowNull,
+ bool fStrict)
+{
+ for (const auto& t : typesExpected) {
+ const UniValue& v = find_value(o, t.first);
+ if (!fAllowNull && v.isNull())
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
+
+ if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
+ std::string err = strprintf("Expected type %s for %s, got %s",
+ uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
+ }
+ }
+
+ if (fStrict)
+ {
+ for (const std::string& k : o.getKeys())
+ {
+ if (typesExpected.count(k) == 0)
+ {
+ std::string err = strprintf("Unexpected key %s", k);
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
+ }
+ }
+ }
+}
+
+CAmount AmountFromValue(const UniValue& value)
+{
+ if (!value.isNum() && !value.isStr())
+ throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
+ CAmount amount;
+ if (!ParseFixedPoint(value.getValStr(), 8, &amount))
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
+ if (!MoneyRange(amount))
+ throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
+ return amount;
+}
+
+uint256 ParseHashV(const UniValue& v, std::string strName)
+{
+ std::string strHex(v.get_str());
+ if (64 != strHex.length())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
+ if (!IsHex(strHex)) // Note: IsHex("") is false
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ return uint256S(strHex);
+}
+uint256 ParseHashO(const UniValue& o, std::string strKey)
+{
+ return ParseHashV(find_value(o, strKey), strKey);
+}
+std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
+{
+ std::string strHex;
+ if (v.isStr())
+ strHex = v.get_str();
+ if (!IsHex(strHex))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ return ParseHex(strHex);
+}
+std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
+{
+ return ParseHexV(find_value(o, strKey), strKey);
+}
+
+std::string HelpExampleCli(const std::string& methodname, const std::string& args)
+{
+ return "> bitcoin-cli " + methodname + " " + args + "\n";
+}
+
+std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
+{
+ return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
+ "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
+}
+
// Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string& hex_in)
{
@@ -79,7 +183,7 @@ public:
return UniValue(UniValue::VOBJ);
}
- UniValue operator()(const CKeyID& keyID) const
+ UniValue operator()(const PKHash& keyID) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", false);
@@ -87,7 +191,7 @@ public:
return obj;
}
- UniValue operator()(const CScriptID& scriptID) const
+ UniValue operator()(const ScriptHash& scriptID) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", true);
@@ -167,6 +271,10 @@ UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_s
}
}
+/**
+ * A pair of strings that can be aligned (through padding) with other Sections
+ * later on
+ */
struct Section {
Section(const std::string& left, const std::string& right)
: m_left{left}, m_right{right} {}
@@ -174,6 +282,10 @@ struct Section {
const std::string m_right;
};
+/**
+ * Keeps track of RPCArgs by transforming them into sections for the purpose
+ * of serializing everything to a single string
+ */
struct Sections {
std::vector<Section> m_sections;
size_t m_max_pad{0};
@@ -184,16 +296,26 @@ struct Sections {
m_sections.push_back(s);
}
+ /**
+ * Serializing RPCArgs depends on the outer type. Only arrays and
+ * dictionaries can be nested in json. The top-level outer type is "named
+ * arguments", a mix between a dictionary and arrays.
+ */
enum class OuterType {
ARR,
OBJ,
NAMED_ARG, // Only set on first recursion
};
+ /**
+ * Recursive helper to translate an RPCArg into sections
+ */
void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG)
{
const auto indent = std::string(current_indent, ' ');
const auto indent_next = std::string(current_indent + 2, ' ');
+ const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
+
switch (arg.m_type) {
case RPCArg::Type::STR_HEX:
case RPCArg::Type::STR:
@@ -203,10 +325,10 @@ struct Sections {
case RPCArg::Type::BOOL: {
if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
- if (arg.m_type_str.size() != 0 && outer_type == OuterType::OBJ) {
+ if (arg.m_type_str.size() != 0 && push_name) {
left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0);
} else {
- left += outer_type == OuterType::OBJ ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
+ left += push_name ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
}
left += ",";
PushSection({left, arg.ToDescriptionString()});
@@ -215,7 +337,7 @@ struct Sections {
case RPCArg::Type::OBJ:
case RPCArg::Type::OBJ_USER_KEYS: {
const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
- PushSection({indent + "{", right});
+ PushSection({indent + (push_name ? "\"" + arg.m_name + "\": " : "") + "{", right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::OBJ);
}
@@ -227,7 +349,7 @@ struct Sections {
}
case RPCArg::Type::ARR: {
auto left = indent;
- left += outer_type == OuterType::OBJ ? "\"" + arg.m_name + "\": " : "";
+ left += push_name ? "\"" + arg.m_name + "\": " : "";
left += "[";
const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
PushSection({left, right});
@@ -243,6 +365,9 @@ struct Sections {
}
}
+ /**
+ * Concatenate all sections with proper padding
+ */
std::string ToString() const
{
std::string ret;
diff --git a/src/rpc/util.h b/src/rpc/util.h
index d2edf30e02..e4fa8fc3d7 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -26,6 +26,48 @@ struct InitInterfaces;
//! state to RPC method implementations.
extern InitInterfaces* g_rpc_interfaces;
+/** Wrapper for UniValue::VType, which includes typeAny:
+ * Used to denote don't care type. */
+struct UniValueType {
+ UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
+ UniValueType() : typeAny(true) {}
+ bool typeAny;
+ UniValue::VType type;
+};
+
+/**
+ * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
+ * the right number of arguments are passed, just that any passed are the correct type.
+ */
+void RPCTypeCheck(const UniValue& params,
+ const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
+
+/**
+ * Type-check one argument; throws JSONRPCError if wrong type given.
+ */
+void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
+
+/*
+ Check for expected keys/value types in an Object.
+*/
+void RPCTypeCheckObj(const UniValue& o,
+ const std::map<std::string, UniValueType>& typesExpected,
+ bool fAllowNull = false,
+ bool fStrict = false);
+
+/**
+ * Utilities: convert hex-encoded Values
+ * (throws error if not hex).
+ */
+extern uint256 ParseHashV(const UniValue& v, std::string strName);
+extern uint256 ParseHashO(const UniValue& o, std::string strKey);
+extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
+extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
+
+extern CAmount AmountFromValue(const UniValue& value);
+extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
+extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
+
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);