diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/blockchain.cpp | 265 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 7 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 12 | ||||
-rw-r--r-- | src/rpc/net.cpp | 8 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 29 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.cpp | 16 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.h | 16 | ||||
-rw-r--r-- | src/rpc/util.cpp | 14 |
9 files changed, 166 insertions, 202 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index b7dcd59c6d..9513c2b9ac 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -10,6 +10,7 @@ #include <chain.h> #include <chainparams.h> #include <coins.h> +#include <node/coinstats.h> #include <consensus/validation.h> #include <core_io.h> #include <hash.h> @@ -374,6 +375,7 @@ static std::string EntryDescriptionString() return " \"vsize\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n" " \"size\" : n, (numeric) (DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n" " size will be completely removed in v0.20.\n" + " \"weight\" : n, (numeric) transaction weight as defined in BIP 141.\n" " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n" " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" @@ -413,6 +415,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool info.pushKV("vsize", (int)e.GetTxSize()); if (IsDeprecatedRPCEnabled("size")) info.pushKV("size", (int)e.GetTxSize()); + info.pushKV("weight", (int)e.GetTxWeight()); info.pushKV("fee", ValueFromAmount(e.GetFee())); info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); info.pushKV("time", e.GetTime()); @@ -907,77 +910,6 @@ static UniValue getblock(const JSONRPCRequest& request) return blockToJSON(block, tip, pblockindex, verbosity >= 2); } -struct CCoinsStats -{ - int nHeight; - uint256 hashBlock; - uint64_t nTransactions; - uint64_t nTransactionOutputs; - uint64_t nBogoSize; - uint256 hashSerialized; - uint64_t nDiskSize; - CAmount nTotalAmount; - - CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount(0) {} -}; - -static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs) -{ - assert(!outputs.empty()); - ss << hash; - ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u); - stats.nTransactions++; - for (const auto& output : outputs) { - ss << VARINT(output.first + 1); - ss << output.second.out.scriptPubKey; - ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED); - stats.nTransactionOutputs++; - stats.nTotalAmount += output.second.out.nValue; - stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ + - 2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */; - } - ss << VARINT(0u); -} - -//! Calculate statistics about the unspent transaction output set -static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) -{ - std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor()); - assert(pcursor); - - CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - stats.hashBlock = pcursor->GetBestBlock(); - { - LOCK(cs_main); - stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; - } - ss << stats.hashBlock; - uint256 prevkey; - std::map<uint32_t, Coin> outputs; - while (pcursor->Valid()) { - boost::this_thread::interruption_point(); - COutPoint key; - Coin coin; - if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { - if (!outputs.empty() && key.hash != prevkey) { - ApplyStats(stats, ss, prevkey, outputs); - outputs.clear(); - } - prevkey = key.hash; - outputs[key.n] = std::move(coin); - } else { - return error("%s: unable to read value", __func__); - } - pcursor->Next(); - } - if (!outputs.empty()) { - ApplyStats(stats, ss, prevkey, outputs); - } - stats.hashSerialized = ss.GetHash(); - stats.nDiskSize = view->EstimateSize(); - return true; -} - static UniValue pruneblockchain(const JSONRPCRequest& request) { RPCHelpMan{"pruneblockchain", "", @@ -1062,7 +994,9 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) CCoinsStats stats; ::ChainstateActive().ForceFlushStateToDisk(); - if (GetUTXOStats(pcoinsdbview.get(), stats)) { + + CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB()); + if (GetUTXOStats(coins_view, stats)) { ret.pushKV("height", (int64_t)stats.nHeight); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("transactions", (int64_t)stats.nTransactions); @@ -1126,19 +1060,21 @@ UniValue gettxout(const JSONRPCRequest& request) fMempool = request.params[2].get_bool(); Coin coin; + CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip(); + if (fMempool) { LOCK(mempool.cs); - CCoinsViewMemPool view(pcoinsTip.get(), mempool); + CCoinsViewMemPool view(coins_view, mempool); if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { return NullUniValue; } } else { - if (!pcoinsTip->GetCoin(out, coin)) { + if (!coins_view->GetCoin(out, coin)) { return NullUniValue; } } - const CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock()); + const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock()); ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.nHeight == MEMPOOL_HEIGHT) { ret.pushKV("confirmations", 0); @@ -1180,57 +1116,53 @@ static UniValue verifychain(const JSONRPCRequest& request) if (!request.params[1].isNull()) nCheckDepth = request.params[1].get_int(); - return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth); + return CVerifyDB().VerifyDB( + Params(), &::ChainstateActive().CoinsTip(), nCheckLevel, nCheckDepth); } -/** Implementation of IsSuperMajority with better feedback */ -static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams) +static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - UniValue rv(UniValue::VOBJ); - bool activated = false; - switch(version) - { - case 2: - activated = pindex->nHeight >= consensusParams.BIP34Height; - break; - case 3: - activated = pindex->nHeight >= consensusParams.BIP66Height; - break; - case 4: - activated = pindex->nHeight >= consensusParams.BIP65Height; - break; - } - rv.pushKV("status", activated); - return rv; -} + // For buried deployments. + // A buried deployment is one where the height of the activation has been hardcoded into + // the client implementation long after the consensus change has activated. See BIP 90. + // Buried deployments with activation height value of + // std::numeric_limits<int>::max() are disabled and thus hidden. + if (height == std::numeric_limits<int>::max()) return; -static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams) -{ UniValue rv(UniValue::VOBJ); - rv.pushKV("id", name); - rv.pushKV("version", version); - rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams)); - return rv; + rv.pushKV("type", "buried"); + // getblockchaininfo reports the softfork as active from when the chain height is + // one below the activation height + rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height); + rv.pushKV("height", height); + softforks.pushKV(name, rv); } -static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - UniValue rv(UniValue::VOBJ); + // For BIP9 deployments. + // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden. + // A timeout value of 0 guarantees a softfork will never be activated. + // This is used when merging logic to implement a proposed softfork without a specified deployment schedule. + if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return; + + UniValue bip9(UniValue::VOBJ); const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id); switch (thresholdState) { - case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break; - case ThresholdState::STARTED: rv.pushKV("status", "started"); break; - case ThresholdState::LOCKED_IN: rv.pushKV("status", "locked_in"); break; - case ThresholdState::ACTIVE: rv.pushKV("status", "active"); break; - case ThresholdState::FAILED: rv.pushKV("status", "failed"); break; + case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break; + case ThresholdState::STARTED: bip9.pushKV("status", "started"); break; + case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break; + case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break; + case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break; } if (ThresholdState::STARTED == thresholdState) { - rv.pushKV("bit", consensusParams.vDeployments[id].bit); + bip9.pushKV("bit", consensusParams.vDeployments[id].bit); } - rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime); - rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); - rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id)); + bip9.pushKV("startTime", consensusParams.vDeployments[id].nStartTime); + bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); + int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id); + bip9.pushKV("since", since_height); if (ThresholdState::STARTED == thresholdState) { UniValue statsUV(UniValue::VOBJ); @@ -1240,18 +1172,18 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse statsUV.pushKV("elapsed", statsStruct.elapsed); statsUV.pushKV("count", statsStruct.count); statsUV.pushKV("possible", statsStruct.possible); - rv.pushKV("statistics", statsUV); + bip9.pushKV("statistics", statsUV); } - return rv; -} -static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) -{ - // Deployments with timeout value of 0 are hidden. - // A timeout value of 0 guarantees a softfork will never be activated. - // This is used when softfork codes are merged without specifying the deployment schedule. - if (consensusParams.vDeployments[id].nTimeout > 0) - bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id)); + UniValue rv(UniValue::VOBJ); + rv.pushKV("type", "bip9"); + rv.pushKV("bip9", bip9); + if (ThresholdState::ACTIVE == thresholdState) { + rv.pushKV("height", since_height); + } + rv.pushKV("active", ThresholdState::ACTIVE == thresholdState); + + softforks.pushKV(name, rv); } UniValue getblockchaininfo(const JSONRPCRequest& request) @@ -1275,29 +1207,25 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" " \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" " \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" - " \"softforks\": [ (array) status of softforks in progress\n" - " {\n" - " \"id\": \"xxxx\", (string) name of softfork\n" - " \"version\": xx, (numeric) block version\n" - " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n" - " \"status\": xx, (boolean) true if threshold reached\n" - " },\n" - " }, ...\n" - " ],\n" - " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n" + " \"softforks\": { (object) status of softforks\n" " \"xxxx\" : { (string) name of the softfork\n" - " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" - " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" - " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" - " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" - " \"since\": xx, (numeric) height of the first block to which the status applies\n" - " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n" - " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n" - " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" - " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" - " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n" - " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" - " }\n" + " \"type\": \"xxxx\", (string) one of \"buried\", \"bip9\"\n" + " \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n" + " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" + " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" + " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" + " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" + " \"since\": xx, (numeric) height of the first block to which the status applies\n" + " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n" + " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n" + " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" + " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" + " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n" + " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" + " }\n" + " },\n" + " \"height\": \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n" + " \"active\": xx, (boolean) true if the rules are enforced for the mempool and the next block\n" " }\n" " }\n" " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" @@ -1342,16 +1270,14 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) } const Consensus::Params& consensusParams = Params().GetConsensus(); - UniValue softforks(UniValue::VARR); - UniValue bip9_softforks(UniValue::VOBJ); - softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) { - BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos)); - } + UniValue softforks(UniValue::VOBJ); + BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height); + BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height); + BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height); + BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight); + BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight); + BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); obj.pushKV("softforks", softforks); - obj.pushKV("bip9_softforks", bip9_softforks); obj.pushKV("warnings", GetWarnings("statusbar")); return obj; @@ -1643,6 +1569,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" " \"window_final_block_hash\": \"...\", (string) The hash of the final block in the window.\n" + " \"window_final_block_height\": xxxxx, (numeric) The height of the final block in the window.\n" " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" @@ -1693,6 +1620,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) ret.pushKV("time", (int64_t)pindex->nTime); ret.pushKV("txcount", (int64_t)pindex->nChainTx); ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex()); + ret.pushKV("window_final_block_height", pindex->nHeight); ret.pushKV("window_block_count", blockcount); if (blockcount > 0) { ret.pushKV("window_tx_count", nTxDiff); @@ -2136,17 +2064,21 @@ UniValue scantxoutset(const JSONRPCRequest& request) }, RPCResult{ "{\n" + " \"success\": true|false, (boolean) Whether the scan was completed\n" + " \"txouts\": n, (numeric) The number of unspent transaction outputs scanned\n" + " \"height\": n, (numeric) The current block height (index)\n" + " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n" " \"unspents\": [\n" - " {\n" - " \"txid\" : \"transactionid\", (string) The transaction id\n" - " \"vout\": n, (numeric) the vout value\n" - " \"scriptPubKey\" : \"script\", (string) the script key\n" - " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n" - " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n" - " \"height\" : n, (numeric) Height of the unspent transaction output\n" + " {\n" + " \"txid\": \"hash\", (string) The transaction id\n" + " \"vout\": n, (numeric) The vout value\n" + " \"scriptPubKey\": \"script\", (string) The script key\n" + " \"desc\": \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n" + " \"amount\": x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n" + " \"height\": n, (numeric) Height of the unspent transaction output\n" " }\n" - " ,...], \n" - " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" + " ,...],\n" + " \"total_amount\": x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" "]\n" }, RPCExamples{""}, @@ -2200,15 +2132,20 @@ UniValue scantxoutset(const JSONRPCRequest& request) g_scan_progress = 0; int64_t count = 0; std::unique_ptr<CCoinsViewCursor> pcursor; + CBlockIndex* tip; { LOCK(cs_main); ::ChainstateActive().ForceFlushStateToDisk(); - pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor()); + pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor()); assert(pcursor); + tip = ::ChainActive().Tip(); + assert(tip); } bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins); result.pushKV("success", res); - result.pushKV("searched_items", count); + result.pushKV("txouts", count); + result.pushKV("height", tip->nHeight); + result.pushKV("bestblock", tip->GetBlockHash().GetHex()); for (const auto& it : coins) { const COutPoint& outpoint = it.first; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 3cd661e067..93fca5a6de 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -85,6 +85,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblockheader", 1, "verbose" }, { "getchaintxstats", 0, "nblocks" }, { "gettransaction", 1, "include_watchonly" }, + { "gettransaction", 2, "decode" }, { "getrawtransaction", 1, "verbose" }, { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 48bc88823a..07c2958635 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -352,7 +352,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) "}\n" }, RPCExamples{ - HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}") + HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'") + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}") }, }.Check(request); @@ -482,9 +482,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? } - const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT]; // GBT must be called with 'segwit' set in the rules - if (setClientRules.count(segwit_info.name) != 1) { + if (setClientRules.count("segwit") != 1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})"); } @@ -521,7 +520,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) pblock->nNonce = 0; // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration - const bool fPreSegWit = (ThresholdState::ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache)); + const bool fPreSegWit = (pindexPrev->nHeight + 1 < consensusParams.SegwitHeight); UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 6be4057366..1516007201 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -136,6 +136,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request) RPCResult{ "{\n" " \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n" + " \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n" " \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n" " \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n" " \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n" @@ -149,13 +150,15 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request) RPCTypeCheck(request.params, {UniValue::VSTR}); FlatSigningProvider provider; - auto desc = Parse(request.params[0].get_str(), provider); + std::string error; + auto desc = Parse(request.params[0].get_str(), provider, error); if (!desc) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor")); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } UniValue result(UniValue::VOBJ); result.pushKV("descriptor", desc->ToString()); + result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str())); result.pushKV("isrange", desc->IsRange()); result.pushKV("issolvable", desc->IsSolvable()); result.pushKV("hasprivatekeys", provider.keys.size() > 0); @@ -197,9 +200,10 @@ UniValue deriveaddresses(const JSONRPCRequest& request) } FlatSigningProvider key_provider; - auto desc = Parse(desc_str, key_provider, /* require_checksum = */ true); + std::string error; + auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true); if (!desc) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor")); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } if (!desc->IsRange() && request.params.size() > 1) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 16b59e3d58..25dda924a4 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -9,6 +9,7 @@ #include <core_io.h> #include <net.h> #include <net_processing.h> +#include <net_permissions.h> #include <netbase.h> #include <policy/policy.h> #include <policy/settings.h> @@ -177,7 +178,12 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) } obj.pushKV("inflight", heights); } - obj.pushKV("whitelisted", stats.fWhitelisted); + obj.pushKV("whitelisted", stats.m_legacyWhitelisted); + UniValue permissions(UniValue::VARR); + for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) { + permissions.push_back(permission); + } + obj.pushKV("permissions", permissions); obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter)); UniValue sendPerMsgCmd(UniValue::VOBJ); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 0ab504de06..fb8ea8c227 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -259,7 +259,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) // Loop through txids and try to find which block they're in. Exit loop once a block is found. for (const auto& tx : setTxids) { - const Coin& coin = AccessByTxid(*pcoinsTip, tx); + const Coin& coin = AccessByTxid(::ChainstateActive().CoinsTip(), tx); if (!coin.IsSpent()) { pblockindex = ::ChainActive()[coin.nHeight]; break; @@ -406,7 +406,11 @@ static UniValue createrawtransaction(const JSONRPCRequest& request) }, true ); - CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]); + bool rbf = false; + if (!request.params[3].isNull()) { + rbf = request.params[3].isTrue(); + } + CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf); return EncodeHexTx(CTransaction(rawTx)); } @@ -632,7 +636,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) { LOCK(cs_main); LOCK(mempool.cs); - CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view @@ -754,7 +758,10 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) } FindCoins(coins); - return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]); + // Parse the prevtxs array + ParsePrevouts(request.params[2], &keystore, coins); + + return SignTransaction(mtx, &keystore, coins, request.params[3]); } static UniValue sendrawtransaction(const JSONRPCRequest& request) @@ -810,14 +817,14 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) max_raw_tx_fee = fr.GetFee((weight+3)/4); } - uint256 txid; std::string err_string; - const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee); + AssertLockNotHeld(cs_main); + const TransactionError err = BroadcastTransaction(tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true); if (TransactionError::OK != err) { throw JSONRPCTransactionError(err, err_string); } - return txid.GetHex(); + return tx->GetHash().GetHex(); } static UniValue testmempoolaccept(const JSONRPCRequest& request) @@ -1365,7 +1372,11 @@ UniValue createpsbt(const JSONRPCRequest& request) }, true ); - CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]); + bool rbf = false; + if (!request.params[3].isNull()) { + rbf = request.params[3].isTrue(); + } + CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf); // Make a blank psbt PartiallySignedTransaction psbtx; @@ -1497,7 +1508,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request) CCoinsViewCache view(&viewDummy); { LOCK2(cs_main, mempool.cs); - CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index 1c96d01232..697c6d45c4 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -19,7 +19,7 @@ #include <util/rbf.h> #include <util/strencodings.h> -CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf) +CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf) { if (inputs_in.isNull() || outputs_in.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); @@ -37,8 +37,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal 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(); @@ -53,7 +51,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); uint32_t nSequence; - if (rbfOptIn) { + if (rbf) { nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */ } else if (rawTx.nLockTime) { nSequence = CTxIn::SEQUENCE_FINAL - 1; @@ -125,7 +123,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal } } - if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) { + if (rbf && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); } @@ -149,9 +147,8 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std:: vErrorsRet.push_back(entry); } -UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType) +void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& 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) { @@ -199,7 +196,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival } // 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())) { + if (keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { RPCTypeCheckObj(prevOut, { {"redeemScript", UniValueType(UniValue::VSTR)}, @@ -228,7 +225,10 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival } } } +} +UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType) +{ int nHashType = ParseSighashString(hashType); bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index d198887b93..b35e6da4ca 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -12,21 +12,29 @@ class UniValue; struct CMutableTransaction; class Coin; class COutPoint; +class SigningProvider; /** * 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, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType); +UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType); + +/** + * Parse a prevtxs UniValue array and get the map of coins from it + * + * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain + * @param keystore A pointer to the temprorary keystore if there is one + * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call + */ +void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins); /** Create a transaction from univalue parameters */ -CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf); +CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf); #endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index de90276677..22d67c34da 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -4,11 +4,12 @@ #include <key_io.h> #include <outputtype.h> -#include <script/signingprovider.h> #include <rpc/util.h> #include <script/descriptor.h> +#include <script/signingprovider.h> #include <tinyformat.h> #include <util/strencodings.h> +#include <util/string.h> #include <tuple> @@ -645,11 +646,7 @@ std::string RPCArg::ToString(const bool oneline) const } case Type::OBJ: case Type::OBJ_USER_KEYS: { - std::string res; - for (size_t i = 0; i < m_inner.size();) { - res += m_inner[i].ToStringObj(oneline); - if (++i < m_inner.size()) res += ","; - } + const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); }); if (m_type == Type::OBJ) { return "{" + res + "}"; } else { @@ -717,9 +714,10 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object"); } - auto desc = Parse(desc_str, provider); + std::string error; + auto desc = Parse(desc_str, provider, error); if (!desc) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str)); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } if (!desc->IsRange()) { range.first = 0; |