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