Bitcoin Core  31.0.0
P2P Digital Currency
txoutproof.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-present 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 
22 
24 {
25  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  {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The txids to filter",
34  {
35  {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
36  },
37  },
38  {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "If specified, looks for txid in the block with this hash"},
39  },
40  RPCResult{
41  RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
42  },
43  RPCExamples{""},
44  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
45  {
46  std::set<Txid> setTxids;
47  UniValue txids = request.params[0].get_array();
48  if (txids.empty()) {
49  throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter 'txids' cannot be empty");
50  }
51  for (unsigned int idx = 0; idx < txids.size(); idx++) {
52  auto ret{setTxids.insert(Txid::FromUint256(ParseHashV(txids[idx], "txid")))};
53  if (!ret.second) {
54  throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + txids[idx].get_str());
55  }
56  }
57 
58  const CBlockIndex* pblockindex = nullptr;
59  uint256 hashBlock;
60  ChainstateManager& chainman = EnsureAnyChainman(request.context);
61  if (!request.params[1].isNull()) {
62  LOCK(cs_main);
63  hashBlock = ParseHashV(request.params[1], "blockhash");
64  pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
65  if (!pblockindex) {
66  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
67  }
68  } else {
69  LOCK(cs_main);
70  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  for (const auto& tx : setTxids) {
74  const Coin& coin{AccessByTxid(active_chainstate.CoinsTip(), tx)};
75  if (!coin.IsSpent()) {
76  pblockindex = active_chainstate.m_chain[coin.nHeight];
77  break;
78  }
79  }
80  }
81 
82 
83  // Allow txindex to catch up if we need to query it and before we acquire cs_main.
84  if (g_txindex && !pblockindex) {
85  g_txindex->BlockUntilSyncedToCurrentChain();
86  }
87 
88  if (pblockindex == nullptr) {
89  const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), chainman.m_blockman, hashBlock);
90  if (!tx || hashBlock.IsNull()) {
91  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
92  }
93 
94  LOCK(cs_main);
95  pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
96  if (!pblockindex) {
97  throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
98  }
99  }
100 
101  {
102  LOCK(cs_main);
103  CheckBlockDataAvailability(chainman.m_blockman, *pblockindex, /*check_for_undo=*/false);
104  }
105  CBlock block;
106  if (!chainman.m_blockman.ReadBlock(block, *pblockindex)) {
107  throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
108  }
109 
110  unsigned int ntxFound = 0;
111  for (const auto& tx : block.vtx) {
112  if (setTxids.contains(tx->GetHash())) {
113  ntxFound++;
114  }
115  }
116  if (ntxFound != setTxids.size()) {
117  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block");
118  }
119 
120  DataStream ssMB{};
121  CMerkleBlock mb(block, setTxids);
122  ssMB << mb;
123  std::string strHex = HexStr(ssMB);
124  return strHex;
125  },
126  };
127 }
128 
130 {
131  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  {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"},
137  },
138  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  },
144  RPCExamples{""},
145  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
146  {
147  CMerkleBlock merkleBlock;
148  SpanReader{ParseHexV(request.params[0], "proof")} >> merkleBlock;
149 
151 
152  std::vector<Txid> vMatch;
153  std::vector<unsigned int> vIndex;
154  if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot)
155  return res;
156 
157  ChainstateManager& chainman = EnsureAnyChainman(request.context);
158  LOCK(cs_main);
159 
160  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash());
161  if (!pindex || !chainman.ActiveChain().Contains(pindex) || pindex->nTx == 0) {
162  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
163  }
164 
165  // Check if proof is valid, only add results if so
166  if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) {
167  for (const auto& txid : vMatch) {
168  res.push_back(txid.GetHex());
169  }
170  }
171 
172  return res;
173  },
174  };
175 }
176 
178 {
179  static const CRPCCommand commands[]{
180  {"blockchain", &gettxoutproof},
181  {"blockchain", &verifytxoutproof},
182  };
183  for (const auto& c : commands) {
184  t.appendCommand(c.name, &c);
185  }
186 }
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:686
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1038
void push_back(UniValue val)
Definition: univalue.cpp:103
int ret
static RPCHelpMan gettxoutproof()
Definition: txoutproof.cpp:23
CBlockHeader header
Public only for unit testing.
Definition: merkleblock.h:130
RPC command dispatcher.
Definition: server.h:86
Required arg.
A UTXO entry.
Definition: coins.h:34
Definition: block.h:73
Interface for managing multiple Chainstate objects, where each chainstate is associated with chainsta...
Definition: validation.h:939
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1165
void RegisterTxoutProofRPCCommands(CRPCTable &t)
Definition: txoutproof.cpp:177
const UniValue & get_array() const
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:625
Invalid, missing or duplicate parameter.
Definition: protocol.h:44
const Coin & AccessByTxid(const CCoinsViewCache &view, const Txid &txid)
Utility function to find any unspent output with a given txid.
Definition: coins.cpp:386
Minimal stream for reading from an existing byte array by std::span.
Definition: streams.h:82
Special type that is a STR with only hex chars.
Used to relay blocks as header + vector<merkle branch> to filtered nodes.
Definition: merkleblock.h:126
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:70
Special string with only hex chars.
Chainstate stores and provides an API to update our local knowledge of the current best chain...
Definition: validation.h:550
uint256 hashMerkleRoot
Definition: block.h:32
#define LOCK(cs)
Definition: sync.h:258
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:132
std::unique_ptr< TxIndex > g_txindex
The global transaction index, used in GetTransaction. May be null.
Definition: txindex.cpp:34
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:410
bool empty() const
Definition: univalue.h:69
unsigned int GetNumTransactions() const
Get number of transactions the merkle proof is indicating for cross-reference with local blockchain k...
Definition: merkleblock.h:115
Invalid address or key.
Definition: protocol.h:42
CPartialMerkleTree txn
Definition: merkleblock.h:131
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
constexpr bool IsNull() const
Definition: uint256.h:48
CTransactionRef GetTransaction(const CBlockIndex *const block_index, const CTxMemPool *const mempool, const Txid &hash, const BlockManager &blockman, uint256 &hashBlock)
Return transaction with a given hash.
uint256 GetHash() const
Definition: block.cpp:15
256-bit opaque blob.
Definition: uint256.h:195
Optional argument for which the default value is omitted from help text for one of two reasons: ...
std::vector< CTransactionRef > vtx
Definition: block.h:77
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: chain.h:93
static transaction_identifier FromUint256(const uint256 &id)
uint256 ParseHashV(const UniValue &v, std::string_view name)
Utilities: convert hex-encoded Values (throws error if not hex).
Definition: util.cpp:117
uint256 ExtractMatches(std::vector< Txid > &vMatch, std::vector< unsigned int > &vnIndex)
extract the matching txid&#39;s represented by this partial merkle tree and their respective indices with...
bool ReadBlock(CBlock &block, const FlatFilePos &pos, const std::optional< uint256 > &expected_hash) const
Functions for disk access for blocks.
size_t size() const
Definition: univalue.h:71
void CheckBlockDataAvailability(BlockManager &blockman, const CBlockIndex &blockindex, bool check_for_undo)
Definition: blockchain.cpp:671
Chainstate & ActiveChainstate() const
Alternatives to CurrentChainstate() used by older code to query latest chainstate information without...
static RPCHelpMan verifytxoutproof()
Definition: txoutproof.cpp:129
ChainstateManager & EnsureAnyChainman(const std::any &context)
Definition: server_util.cpp:82
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: cs_main.cpp:8
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
unsigned int nTx
Number of transactions in this block.
Definition: chain.h:123
std::vector< unsigned char > ParseHexV(const UniValue &v, std::string_view name)
Definition: util.cpp:130