LCOV - code coverage report
Current view: top level - src/rpc - txoutproof.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 129 136 94.9 %
Date: 2022-04-21 14:51:19 Functions: 5 5 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 41 48 85.4 %

           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 : }

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a