Branch data Line data Source code
# 1 : : // Copyright (c) 2010 Satoshi Nakamoto
# 2 : : // Copyright (c) 2009-2022 The Bitcoin Core developers
# 3 : : // Distributed under the MIT software license, see the accompanying
# 4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 5 : :
# 6 : : #include <chain.h>
# 7 : : #include <chainparams.h>
# 8 : : #include <coins.h>
# 9 : : #include <index/txindex.h>
# 10 : : #include <merkleblock.h>
# 11 : : #include <node/blockstorage.h>
# 12 : : #include <primitives/transaction.h>
# 13 : : #include <rpc/server.h>
# 14 : : #include <rpc/server_util.h>
# 15 : : #include <rpc/util.h>
# 16 : : #include <univalue.h>
# 17 : : #include <util/strencodings.h>
# 18 : : #include <validation.h>
# 19 : :
# 20 : : using node::GetTransaction;
# 21 : : using node::ReadBlockFromDisk;
# 22 : :
# 23 : : static RPCHelpMan gettxoutproof()
# 24 : 1619 : {
# 25 : 1619 : return RPCHelpMan{"gettxoutproof",
# 26 : 1619 : "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
# 27 : 1619 : "\nNOTE: By default this function only works sometimes. This is when there is an\n"
# 28 : 1619 : "unspent output in the utxo for this transaction. To make it always work,\n"
# 29 : 1619 : "you need to maintain a transaction index, using the -txindex command line option or\n"
# 30 : 1619 : "specify the block in which the transaction is included manually (by blockhash).\n",
# 31 : 1619 : {
# 32 : 1619 : {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The txids to filter",
# 33 : 1619 : {
# 34 : 1619 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
# 35 : 1619 : },
# 36 : 1619 : },
# 37 : 1619 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"},
# 38 : 1619 : },
# 39 : 1619 : RPCResult{
# 40 : 1619 : RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
# 41 : 1619 : },
# 42 : 1619 : RPCExamples{""},
# 43 : 1619 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 44 : 1619 : {
# 45 : 25 : std::set<uint256> setTxids;
# 46 : 25 : UniValue txids = request.params[0].get_array();
# 47 [ + + ]: 25 : if (txids.empty()) {
# 48 : 1 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter 'txids' cannot be empty");
# 49 : 1 : }
# 50 [ + + ]: 54 : for (unsigned int idx = 0; idx < txids.size(); idx++) {
# 51 : 31 : auto ret = setTxids.insert(ParseHashV(txids[idx], "txid"));
# 52 [ + + ]: 31 : if (!ret.second) {
# 53 : 1 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + txids[idx].get_str());
# 54 : 1 : }
# 55 : 31 : }
# 56 : :
# 57 : 23 : const CBlockIndex* pblockindex = nullptr;
# 58 : 23 : uint256 hashBlock;
# 59 : 23 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
# 60 [ + + ]: 23 : if (!request.params[1].isNull()) {
# 61 : 5 : LOCK(cs_main);
# 62 : 5 : hashBlock = ParseHashV(request.params[1], "blockhash");
# 63 : 5 : pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
# 64 [ + + ]: 5 : if (!pblockindex) {
# 65 : 1 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
# 66 : 1 : }
# 67 : 18 : } else {
# 68 : 18 : LOCK(cs_main);
# 69 : 18 : CChainState& active_chainstate = chainman.ActiveChainstate();
# 70 : :
# 71 : : // Loop through txids and try to find which block they're in. Exit loop once a block is found.
# 72 [ + + ]: 18 : for (const auto& tx : setTxids) {
# 73 : 16 : const Coin& coin = AccessByTxid(active_chainstate.CoinsTip(), tx);
# 74 [ + + ]: 16 : if (!coin.IsSpent()) {
# 75 : 13 : pblockindex = active_chainstate.m_chain[coin.nHeight];
# 76 : 13 : break;
# 77 : 13 : }
# 78 : 16 : }
# 79 : 18 : }
# 80 : :
# 81 : :
# 82 : : // Allow txindex to catch up if we need to query it and before we acquire cs_main.
# 83 [ + + ][ + + ]: 22 : if (g_txindex && !pblockindex) {
# 84 : 1 : g_txindex->BlockUntilSyncedToCurrentChain();
# 85 : 1 : }
# 86 : :
# 87 : 22 : LOCK(cs_main);
# 88 : :
# 89 [ + + ]: 22 : if (pblockindex == nullptr) {
# 90 : 3 : const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), Params().GetConsensus(), hashBlock);
# 91 [ + + ][ - + ]: 3 : if (!tx || hashBlock.IsNull()) {
# 92 : 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
# 93 : 2 : }
# 94 : 1 : pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
# 95 [ - + ]: 1 : if (!pblockindex) {
# 96 : 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
# 97 : 0 : }
# 98 : 1 : }
# 99 : :
# 100 : 20 : CBlock block;
# 101 [ - + ]: 20 : if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
# 102 : 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
# 103 : 0 : }
# 104 : :
# 105 : 20 : unsigned int ntxFound = 0;
# 106 [ + + ]: 42 : for (const auto& tx : block.vtx) {
# 107 [ + + ]: 42 : if (setTxids.count(tx->GetHash())) {
# 108 : 21 : ntxFound++;
# 109 : 21 : }
# 110 : 42 : }
# 111 [ + + ]: 20 : if (ntxFound != setTxids.size()) {
# 112 : 1 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block");
# 113 : 1 : }
# 114 : :
# 115 : 19 : CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
# 116 : 19 : CMerkleBlock mb(block, setTxids);
# 117 : 19 : ssMB << mb;
# 118 : 19 : std::string strHex = HexStr(ssMB);
# 119 : 19 : return strHex;
# 120 : 20 : },
# 121 : 1619 : };
# 122 : 1619 : }
# 123 : :
# 124 : : static RPCHelpMan verifytxoutproof()
# 125 : 1607 : {
# 126 : 1607 : return RPCHelpMan{"verifytxoutproof",
# 127 : 1607 : "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
# 128 : 1607 : "and throwing an RPC error if the block is not in our best chain\n",
# 129 : 1607 : {
# 130 : 1607 : {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"},
# 131 : 1607 : },
# 132 : 1607 : RPCResult{
# 133 : 1607 : RPCResult::Type::ARR, "", "",
# 134 : 1607 : {
# 135 : 1607 : {RPCResult::Type::STR_HEX, "txid", "The txid(s) which the proof commits to, or empty array if the proof cannot be validated."},
# 136 : 1607 : }
# 137 : 1607 : },
# 138 : 1607 : RPCExamples{""},
# 139 : 1607 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 140 : 1607 : {
# 141 : 13 : CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
# 142 : 13 : CMerkleBlock merkleBlock;
# 143 : 13 : ssMB >> merkleBlock;
# 144 : :
# 145 : 13 : UniValue res(UniValue::VARR);
# 146 : :
# 147 : 13 : std::vector<uint256> vMatch;
# 148 : 13 : std::vector<unsigned int> vIndex;
# 149 [ - + ]: 13 : if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot)
# 150 : 0 : return res;
# 151 : :
# 152 : 13 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
# 153 : 13 : LOCK(cs_main);
# 154 : :
# 155 : 13 : const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash());
# 156 [ - + ][ - + ]: 13 : if (!pindex || !chainman.ActiveChain().Contains(pindex) || pindex->nTx == 0) {
# [ - + ]
# 157 : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
# 158 : 0 : }
# 159 : :
# 160 : : // Check if proof is valid, only add results if so
# 161 [ + + ]: 13 : if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) {
# 162 [ + + ]: 18 : for (const uint256& hash : vMatch) {
# 163 : 18 : res.push_back(hash.GetHex());
# 164 : 18 : }
# 165 : 11 : }
# 166 : :
# 167 : 13 : return res;
# 168 : 13 : },
# 169 : 1607 : };
# 170 : 1607 : }
# 171 : :
# 172 : : void RegisterTxoutProofRPCCommands(CRPCTable& t)
# 173 : 993 : {
# 174 : 993 : static const CRPCCommand commands[]{
# 175 : : // category actor (function)
# 176 : : // -------- ----------------
# 177 : 993 : {"blockchain", &gettxoutproof},
# 178 : 993 : {"blockchain", &verifytxoutproof},
# 179 : 993 : };
# 180 [ + + ]: 1986 : for (const auto& c : commands) {
# 181 : 1986 : t.appendCommand(c.name, &c);
# 182 : 1986 : }
# 183 : 993 : }
|