Bitcoin Core  29.1.0
P2P Digital Currency
txoutproof.cpp
Go to the documentation of this file.
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 
22 
24 {
25  return RPCHelpMan{"gettxoutproof",
26  "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
27  "\nNOTE: By default this function only works sometimes. This is when there is an\n"
28  "unspent output in the utxo for this transaction. To make it always work,\n"
29  "you need to maintain a transaction index, using the -txindex command line option or\n"
30  "specify the block in which the transaction is included manually (by blockhash).\n",
31  {
32  {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The txids to filter",
33  {
34  {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
35  },
36  },
37  {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "If specified, looks for txid in the block with this hash"},
38  },
39  RPCResult{
40  RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
41  },
42  RPCExamples{""},
43  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
44  {
45  std::set<Txid> setTxids;
46  UniValue txids = request.params[0].get_array();
47  if (txids.empty()) {
48  throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter 'txids' cannot be empty");
49  }
50  for (unsigned int idx = 0; idx < txids.size(); idx++) {
51  auto ret{setTxids.insert(Txid::FromUint256(ParseHashV(txids[idx], "txid")))};
52  if (!ret.second) {
53  throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + txids[idx].get_str());
54  }
55  }
56 
57  const CBlockIndex* pblockindex = nullptr;
58  uint256 hashBlock;
59  ChainstateManager& chainman = EnsureAnyChainman(request.context);
60  if (!request.params[1].isNull()) {
61  LOCK(cs_main);
62  hashBlock = ParseHashV(request.params[1], "blockhash");
63  pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
64  if (!pblockindex) {
65  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
66  }
67  } else {
68  LOCK(cs_main);
69  Chainstate& 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  for (const auto& tx : setTxids) {
73  const Coin& coin{AccessByTxid(active_chainstate.CoinsTip(), tx)};
74  if (!coin.IsSpent()) {
75  pblockindex = active_chainstate.m_chain[coin.nHeight];
76  break;
77  }
78  }
79  }
80 
81 
82  // Allow txindex to catch up if we need to query it and before we acquire cs_main.
83  if (g_txindex && !pblockindex) {
84  g_txindex->BlockUntilSyncedToCurrentChain();
85  }
86 
87  if (pblockindex == nullptr) {
88  const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), hashBlock, chainman.m_blockman);
89  if (!tx || hashBlock.IsNull()) {
90  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
91  }
92 
93  LOCK(cs_main);
94  pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
95  if (!pblockindex) {
96  throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
97  }
98  }
99 
100  {
101  LOCK(cs_main);
102  CheckBlockDataAvailability(chainman.m_blockman, *pblockindex, /*check_for_undo=*/false);
103  }
104  CBlock block;
105  if (!chainman.m_blockman.ReadBlock(block, *pblockindex)) {
106  throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
107  }
108 
109  unsigned int ntxFound = 0;
110  for (const auto& tx : block.vtx) {
111  if (setTxids.count(tx->GetHash())) {
112  ntxFound++;
113  }
114  }
115  if (ntxFound != setTxids.size()) {
116  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block");
117  }
118 
119  DataStream ssMB{};
120  CMerkleBlock mb(block, setTxids);
121  ssMB << mb;
122  std::string strHex = HexStr(ssMB);
123  return strHex;
124  },
125  };
126 }
127 
129 {
130  return RPCHelpMan{"verifytxoutproof",
131  "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
132  "and throwing an RPC error if the block is not in our best chain\n",
133  {
134  {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"},
135  },
136  RPCResult{
137  RPCResult::Type::ARR, "", "",
138  {
139  {RPCResult::Type::STR_HEX, "txid", "The txid(s) which the proof commits to, or empty array if the proof cannot be validated."},
140  }
141  },
142  RPCExamples{""},
143  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
144  {
145  DataStream ssMB{ParseHexV(request.params[0], "proof")};
146  CMerkleBlock merkleBlock;
147  ssMB >> merkleBlock;
148 
150 
151  std::vector<uint256> vMatch;
152  std::vector<unsigned int> vIndex;
153  if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot)
154  return res;
155 
156  ChainstateManager& chainman = EnsureAnyChainman(request.context);
157  LOCK(cs_main);
158 
159  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash());
160  if (!pindex || !chainman.ActiveChain().Contains(pindex) || pindex->nTx == 0) {
161  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
162  }
163 
164  // Check if proof is valid, only add results if so
165  if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) {
166  for (const uint256& hash : vMatch) {
167  res.push_back(hash.GetHex());
168  }
169  }
170 
171  return res;
172  },
173  };
174 }
175 
177 {
178  static const CRPCCommand commands[]{
179  {"blockchain", &gettxoutproof},
180  {"blockchain", &verifytxoutproof},
181  };
182  for (const auto& c : commands) {
183  t.appendCommand(c.name, &c);
184  }
185 }
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:611
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1007
void push_back(UniValue val)
Definition: univalue.cpp:104
int ret
static RPCHelpMan gettxoutproof()
Definition: txoutproof.cpp:23
RPC command dispatcher.
Definition: server.h:126
Required arg.
A UTXO entry.
Definition: coins.h:32
Definition: block.h:68
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:865
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1111
CTransactionRef GetTransaction(const CBlockIndex *const block_index, const CTxMemPool *const mempool, const uint256 &hash, uint256 &hashBlock, const BlockManager &blockman)
Return transaction with a given hash.
bool ReadBlock(CBlock &block, const FlatFilePos &pos) const
Functions for disk access for blocks.
void RegisterTxoutProofRPCCommands(CRPCTable &t)
Definition: txoutproof.cpp:176
const UniValue & get_array() const
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:585
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:351
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:125
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:504
#define LOCK(cs)
Definition: sync.h:257
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:146
std::unique_ptr< TxIndex > g_txindex
The global transaction index, used in GetTransaction. May be null.
Definition: txindex.cpp:16
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:447
bool empty() const
Definition: univalue.h:69
Invalid address or key.
Definition: protocol.h:42
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
constexpr bool IsNull() const
Definition: uint256.h:48
256-bit opaque blob.
Definition: uint256.h:201
Optional argument for which the default value is omitted from help text for one of two reasons: ...
std::vector< CTransactionRef > vtx
Definition: block.h:72
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: chain.h:140
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:120
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:29
size_t size() const
Definition: univalue.h:71
void CheckBlockDataAvailability(BlockManager &blockman, const CBlockIndex &blockindex, bool check_for_undo)
Definition: blockchain.cpp:606
static RPCHelpMan verifytxoutproof()
Definition: txoutproof.cpp:128
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
unsigned int nTx
Number of transactions in this block.
Definition: chain.h:170
SnapshotCompletionResult MaybeCompleteSnapshotValidation() EXCLUSIVE_LOCKS_REQUIRED(const CBlockIndex *GetSnapshotBaseBlock() const EXCLUSIVE_LOCKS_REQUIRED(Chainstate ActiveChainstate)() const
Once the background validation chainstate has reached the height which is the base of the UTXO snapsh...
Definition: validation.h:1110
std::vector< unsigned char > ParseHexV(const UniValue &v, std::string_view name)
Definition: util.cpp:133