diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/blockchain.cpp | 24 | ||||
-rw-r--r-- | src/rpc/client.cpp | 3 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 92 | ||||
-rw-r--r-- | src/rpc/mining.h | 3 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 9 | ||||
-rw-r--r-- | src/rpc/net.cpp | 5 | ||||
-rw-r--r-- | src/rpc/protocol.h | 2 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 141 | ||||
-rw-r--r-- | src/rpc/server.cpp | 10 | ||||
-rw-r--r-- | src/rpc/server.h | 1 |
10 files changed, 190 insertions, 100 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c17ca2fa3a..d65e107e3c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -210,7 +210,7 @@ UniValue waitfornewblock(const JSONRPCRequest& request) + HelpExampleRpc("waitfornewblock", "1000") ); int timeout = 0; - if (request.params.size() > 0) + if (!request.params[0].isNull()) timeout = request.params[0].get_int(); CUpdatedBlock block; @@ -252,7 +252,7 @@ UniValue waitforblock(const JSONRPCRequest& request) 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; @@ -295,7 +295,7 @@ UniValue waitforblockheight(const JSONRPCRequest& request) 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; @@ -434,7 +434,7 @@ UniValue getrawmempool(const JSONRPCRequest& request) ); bool fVerbose = false; - if (request.params.size() > 0) + if (!request.params[0].isNull()) fVerbose = request.params[0].get_bool(); return mempoolToJSON(fVerbose); @@ -467,7 +467,7 @@ UniValue getmempoolancestors(const JSONRPCRequest& request) } 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"); @@ -531,7 +531,7 @@ UniValue getmempooldescendants(const JSONRPCRequest& request) } 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"); @@ -666,7 +666,7 @@ UniValue getblockheader(const JSONRPCRequest& request) 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) @@ -741,7 +741,7 @@ UniValue getblock(const JSONRPCRequest& request) 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 @@ -798,7 +798,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, stats.nTransactions++; for (const auto output : outputs) { ss << VARINT(output.first + 1); - ss << *(const CScriptBase*)(&output.second.out.scriptPubKey); + ss << output.second.out.scriptPubKey; ss << VARINT(output.second.out.nValue); stats.nTransactionOutputs++; stats.nTotalAmount += output.second.out.nValue; @@ -984,7 +984,7 @@ UniValue gettxout(const JSONRPCRequest& request) 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; @@ -1037,9 +1037,9 @@ UniValue verifychain(const JSONRPCRequest& request) 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(Params(), pcoinsTip, nCheckLevel, nCheckDepth); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 775ad4b6c9..4179453782 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -68,6 +68,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblocktemplate", 0, "template_request" }, { "listsinceblock", 1, "target_confirmations" }, { "listsinceblock", 2, "include_watchonly" }, + { "listsinceblock", 3, "include_removed" }, { "sendmany", 1, "amounts" }, { "sendmany", 2, "minconf" }, { "sendmany", 4, "subtractfeefrom" }, @@ -95,6 +96,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "signrawtransaction", 1, "prevtxs" }, { "signrawtransaction", 2, "privkeys" }, { "sendrawtransaction", 1, "allowhighfees" }, + { "combinerawtransaction", 0, "txs" }, { "fundrawtransaction", 1, "options" }, { "gettxout", 1, "n" }, { "gettxout", 2, "include_mempool" }, @@ -114,7 +116,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, { "estimatesmartfee", 0, "nblocks" }, - { "estimatesmartfee", 1, "conservative" }, { "estimaterawfee", 0, "nblocks" }, { "estimaterawfee", 1, "threshold" }, { "prioritisetransaction", 1, "dummy" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 10bb341e54..7d292a4635 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -24,12 +24,23 @@ #include "util.h" #include "utilstrencodings.h" #include "validationinterface.h" +#include "warnings.h" #include <memory> #include <stdint.h> #include <univalue.h> +unsigned int ParseConfirmTarget(const UniValue& value) +{ + int target = value.get_int(); + unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + if (target < 1 || (unsigned int)target > max_target) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target)); + } + return (unsigned int)target; +} + /** * Return average network hashes per second based on the last 'lookup' blocks, * or from the last difficulty change if 'lookup' is nonpositive. @@ -91,7 +102,7 @@ UniValue getnetworkhashps(const JSONRPCRequest& request) ); 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); + 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) @@ -161,7 +172,7 @@ UniValue generatetoaddress(const JSONRPCRequest& request) 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(); } @@ -363,7 +374,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) UniValue lpval = NullUniValue; std::set<std::string> setClientRules; int64_t nMaxVersionPreVB = -1; - 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"); @@ -796,43 +807,59 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "estimatesmartfee nblocks (conservative)\n" + "estimatesmartfee conf_target (\"estimate_mode\")\n" "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" - "confirmation within nblocks blocks if possible and return the number of blocks\n" + "confirmation within conf_target blocks if possible and return the number of blocks\n" "for which the estimate is valid. Uses virtual transaction size as defined\n" "in BIP 141 (witness data is discounted).\n" "\nArguments:\n" - "1. nblocks (numeric)\n" - "2. conservative (bool, optional, default=true) Whether to return a more conservative estimate which\n" - " also satisfies a longer history. A conservative estimate potentially returns a higher\n" - " feerate and is more likely to be sufficient for the desired target, but is not as\n" - " responsive to short term drops in the prevailing fee market\n" + "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n" + "2. \"estimate_mode\" (string, optional, default=CONSERVATIVE) The fee estimate mode.\n" + " Whether to return a more conservative estimate which also satisfies\n" + " a longer history. A conservative estimate potentially returns a\n" + " higher feerate and is more likely to be sufficient for the desired\n" + " target, but is not as responsive to short term drops in the\n" + " prevailing fee market. Must be one of:\n" + " \"UNSET\" (defaults to CONSERVATIVE)\n" + " \"ECONOMICAL\"\n" + " \"CONSERVATIVE\"\n" "\nResult:\n" "{\n" - " \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n" + " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n" + " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" " \"blocks\" : n (numeric) block number where estimate was found\n" "}\n" "\n" - "A negative value is returned if not enough transactions and blocks\n" + "The request target will be clamped between 2 and the highest target\n" + "fee estimation is able to return based on how long it has been running.\n" + "An error is returned if not enough transactions and blocks\n" "have been observed to make an estimate for any number of blocks.\n" - "However it will not return a value below the mempool reject fee.\n" "\nExample:\n" + HelpExampleCli("estimatesmartfee", "6") ); - RPCTypeCheck(request.params, {UniValue::VNUM}); - - int nBlocks = request.params[0].get_int(); + RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR}); + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + unsigned int conf_target = ParseConfirmTarget(request.params[0]); bool conservative = true; if (request.params.size() > 1 && !request.params[1].isNull()) { - RPCTypeCheckArgument(request.params[1], UniValue::VBOOL); - conservative = request.params[1].get_bool(); + FeeEstimateMode fee_mode; + if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); + } + if (fee_mode == FeeEstimateMode::ECONOMICAL) conservative = false; } UniValue result(UniValue::VOBJ); + UniValue errors(UniValue::VARR); FeeCalculation feeCalc; - CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, ::mempool, conservative); - result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); + CFeeRate feeRate = ::feeEstimator.estimateSmartFee(conf_target, &feeCalc, conservative); + if (feeRate != CFeeRate(0)) { + result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK()))); + } else { + errors.push_back("Insufficient data or no feerate found"); + result.push_back(Pair("errors", errors)); + } result.push_back(Pair("blocks", feeCalc.returnedTarget)); return result; } @@ -841,18 +868,18 @@ UniValue estimaterawfee(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "estimaterawfee nblocks (threshold)\n" + "estimaterawfee conf_target (threshold)\n" "\nWARNING: This interface is unstable and may disappear or change!\n" "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n" " implementation of fee estimation. The parameters it can be called with\n" " and the results it returns will change if the internal implementation changes.\n" "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" - "confirmation within nblocks blocks if possible. Uses virtual transaction size as defined\n" - "in BIP 141 (witness data is discounted).\n" + "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n" + "defined in BIP 141 (witness data is discounted).\n" "\nArguments:\n" - "1. nblocks (numeric) Confirmation target in blocks (1 - 1008)\n" + "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n" "2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n" - " confirmed within nblocks in order to consider those feerates as high enough and proceed to check\n" + " confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n" " lower buckets. Default: 0.95\n" "\nResult:\n" "{\n" @@ -880,12 +907,9 @@ UniValue estimaterawfee(const JSONRPCRequest& request) + HelpExampleCli("estimaterawfee", "6 0.9") ); - RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM, UniValue::VNUM}, true); + RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); - int nBlocks = request.params[0].get_int(); - if (nBlocks < 1 || (unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks"); - } + unsigned int conf_target = ParseConfirmTarget(request.params[0]); double threshold = 0.95; if (!request.params[1].isNull()) { threshold = request.params[1].get_real(); @@ -901,9 +925,9 @@ UniValue estimaterawfee(const JSONRPCRequest& request) EstimationResult buckets; // Only output results for horizons which track the target - if ((unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(horizon)) continue; + if (conf_target > ::feeEstimator.HighestTargetTracked(horizon)) continue; - feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets); + feeRate = ::feeEstimator.estimateRawFee(conf_target, threshold, horizon, &buckets); UniValue horizon_result(UniValue::VOBJ); UniValue errors(UniValue::VARR); UniValue passbucket(UniValue::VOBJ); @@ -954,9 +978,9 @@ static const CRPCCommand commands[] = { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} }, { "util", "estimatefee", &estimatefee, true, {"nblocks"} }, - { "util", "estimatesmartfee", &estimatesmartfee, true, {"nblocks", "conservative"} }, + { "util", "estimatesmartfee", &estimatesmartfee, true, {"conf_target", "estimate_mode"} }, - { "hidden", "estimaterawfee", &estimaterawfee, true, {"nblocks", "threshold"} }, + { "hidden", "estimaterawfee", &estimaterawfee, true, {"conf_target", "threshold"} }, }; void RegisterMiningRPCCommands(CRPCTable &t) diff --git a/src/rpc/mining.h b/src/rpc/mining.h index a148d851da..868d7002b5 100644 --- a/src/rpc/mining.h +++ b/src/rpc/mining.h @@ -12,4 +12,7 @@ /** Generate blocks (mine) */ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript); +/** Check bounds on a command line confirm target */ +unsigned int ParseConfirmTarget(const UniValue& value); + #endif diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index fcbbe1ceed..cd93919eb5 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -6,6 +6,7 @@ #include "base58.h" #include "chain.h" #include "clientversion.h" +#include "core_io.h" #include "init.h" #include "validation.h" #include "httpserver.h" @@ -21,6 +22,7 @@ #include "wallet/wallet.h" #include "wallet/walletdb.h" #endif +#include "warnings.h" #include <stdint.h> #ifdef HAVE_MALLOC_INFO @@ -50,6 +52,7 @@ UniValue getinfo(const JSONRPCRequest& request) "\nDEPRECATED. Returns an object containing various state info.\n" "\nResult:\n" "{\n" + " \"deprecation-warning\": \"...\" (string) warning that the getinfo command is deprecated and will be removed in 0.16\n" " \"version\": xxxxx, (numeric) the server version\n" " \"protocolversion\": xxxxx, (numeric) the protocol version\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" @@ -57,7 +60,7 @@ UniValue getinfo(const JSONRPCRequest& request) " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" " \"timeoffset\": xxxxx, (numeric) the time offset\n" " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" + " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" @@ -65,7 +68,7 @@ UniValue getinfo(const JSONRPCRequest& request) " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" " \"relayfee\": x.xxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n" - " \"errors\": \"...\" (string) any error messages\n" + " \"errors\": \"...\" (string) any error messages\n" "}\n" "\nExamples:\n" + HelpExampleCli("getinfo", "") @@ -84,6 +87,8 @@ UniValue getinfo(const JSONRPCRequest& request) GetProxy(NET_IPV4, proxy); UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("deprecation-warning", "WARNING: getinfo is deprecated and will be fully removed in 0.16." + " Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16")); obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); #ifdef ENABLE_WALLET diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index ed452fcb02..6271e0cc83 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -6,6 +6,7 @@ #include "chainparams.h" #include "clientversion.h" +#include "core_io.h" #include "validation.h" #include "net.h" #include "net_processing.h" @@ -18,7 +19,7 @@ #include "util.h" #include "utilstrencodings.h" #include "version.h" - +#include "warnings.h" #include <univalue.h> @@ -310,7 +311,7 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request) std::vector<AddedNodeInfo> 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()) { diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 70f7092cfe..4bd4702d62 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -82,6 +82,8 @@ enum RPCErrorCode RPC_WALLET_WRONG_ENC_STATE = -15, //!< Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) RPC_WALLET_ENCRYPTION_FAILED = -16, //!< Failed to encrypt the wallet RPC_WALLET_ALREADY_UNLOCKED = -17, //!< Wallet is already unlocked + RPC_WALLET_NOT_FOUND = -18, //!< Invalid wallet specified + RPC_WALLET_NOT_SPECIFIED = -19, //!< No wallet specified (error when there are multiple wallets loaded) }; UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b878624df8..b967f2dbf8 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -137,7 +137,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request) // 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; @@ -211,7 +211,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request) CBlockIndex* pblockindex = NULL; uint256 hashBlock; - if (request.params.size() > 1) + if (!request.params[1].isNull()) { hashBlock = uint256S(request.params[1].get_str()); if (!mapBlockIndex.count(hashBlock)) @@ -412,7 +412,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) } } - if (request.params.size() > 3 && rbfOptIn != SignalsOptInRBF(rawTx)) { + if (!request.params[3].isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); } @@ -554,6 +554,93 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std:: vErrorsRet.push_back(entry); } +UniValue combinerawtransaction(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<CMutableTransaction> txVariants(txs.size()); + + for (unsigned int idx = 0; idx < txs.size(); idx++) { + if (!DecodeHexTx(txVariants[idx], txs[idx].get_str(), true)) { + 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(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(&viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + for (const CTxIn& txin : mergedTx.vin) { + view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail. + } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + + // Use CTransaction for the constant parts of the + // transaction to avoid rehashing. + const CTransaction txConst(mergedTx); + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { + CTxIn& txin = mergedTx.vin[i]; + const Coin& coin = view.AccessCoin(txin.prevout); + if (coin.IsSpent()) { + throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent"); + } + const CScript& prevPubKey = coin.out.scriptPubKey; + const CAmount& amount = coin.out.nValue; + + SignatureData sigdata; + + // ... and merge in other signatures: + for (const CMutableTransaction& txv : txVariants) { + if (txv.vin.size() > i) { + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); + } + } + + UpdateTransaction(mergedTx, i, sigdata); + } + + return EncodeHexTx(mergedTx); +} + UniValue signrawtransaction(const JSONRPCRequest& request) { #ifdef ENABLE_WALLET @@ -626,26 +713,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request) #endif RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); - std::vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1")); - CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); - std::vector<CMutableTransaction> txVariants; - while (!ssData.empty()) { - try { - CMutableTransaction tx; - ssData >> tx; - txVariants.push_back(tx); - } - catch (const std::exception&) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - } - } - - if (txVariants.empty()) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); - - // mergedTx will end up with all the signatures; it - // starts as a clone of the rawtx: - CMutableTransaction mergedTx(txVariants[0]); + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); // Fetch previous transactions (inputs): CCoinsView viewDummy; @@ -656,7 +726,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view - for (const CTxIn& txin : mergedTx.vin) { + for (const CTxIn& txin : mtx.vin) { view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail. } @@ -781,10 +851,10 @@ UniValue signrawtransaction(const JSONRPCRequest& request) // Use CTransaction for the constant parts of the // transaction to avoid rehashing. - const CTransaction txConst(mergedTx); + const CTransaction txConst(mtx); // Sign what we can: - for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { - CTxIn& txin = mergedTx.vin[i]; + for (unsigned int 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"); @@ -795,17 +865,11 @@ UniValue signrawtransaction(const JSONRPCRequest& request) SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: - if (!fHashSingle || (i < mergedTx.vout.size())) - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); + if (!fHashSingle || (i < mtx.vout.size())) + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata); + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i)); - // ... 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); + UpdateTransaction(mtx, i, sigdata); ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { @@ -815,7 +879,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) bool fComplete = vErrors.empty(); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", EncodeHexTx(mergedTx))); + result.push_back(Pair("hex", EncodeHexTx(mtx))); result.push_back(Pair("complete", fComplete)); if (!vErrors.empty()) { result.push_back(Pair("errors", vErrors)); @@ -905,6 +969,7 @@ static const CRPCCommand commands[] = { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} }, + { "rawtransactions", "combinerawtransaction", &combinerawtransaction, true, {"txs"} }, { "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ { "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} }, diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 63e4e9c630..58640d69d6 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -123,16 +123,6 @@ CAmount AmountFromValue(const UniValue& value) return amount; } -UniValue ValueFromAmount(const CAmount& amount) -{ - bool sign = amount < 0; - int64_t n_abs = (sign ? -amount : amount); - int64_t quotient = n_abs / COIN; - int64_t remainder = n_abs % COIN; - return UniValue(UniValue::VNUM, - strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder)); -} - uint256 ParseHashV(const UniValue& v, std::string strName) { std::string strHex; diff --git a/src/rpc/server.h b/src/rpc/server.h index b20c827727..dd6f763245 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -185,7 +185,6 @@ extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strNa extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey); extern CAmount AmountFromValue(const UniValue& value); -extern UniValue ValueFromAmount(const CAmount& amount); extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); |