Bitcoin ABC  0.29.2
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 <config.h>
10 #include <index/txindex.h>
11 #include <merkleblock.h>
12 #include <node/blockstorage.h>
13 #include <primitives/transaction.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 
23 
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. "
29  "This is when there is an\n"
30  "unspent output in the utxo for this transaction. To make it always "
31  "work,\n"
32  "you need to maintain a transaction index, using the -txindex command "
33  "line option or\n"
34  "specify the block in which the transaction is included manually (by "
35  "blockhash).\n",
36  {
37  {
38  "txids",
41  "The txids to filter",
42  {
44  "A transaction hash"},
45  },
46  },
47  {"blockhash", RPCArg::Type::STR_HEX,
49  "If specified, looks for txid in the block with this hash"},
50  },
51  RPCResult{
52  RPCResult::Type::STR, "data",
53  "A string that is a serialized, hex-encoded data for the proof."},
54  RPCExamples{""},
55  [&](const RPCHelpMan &self, const Config &config,
56  const JSONRPCRequest &request) -> UniValue {
57  std::set<TxId> setTxIds;
58  TxId oneTxId;
59  UniValue txids = request.params[0].get_array();
60  for (unsigned int idx = 0; idx < txids.size(); idx++) {
61  const UniValue &utxid = txids[idx];
62  TxId txid(ParseHashV(utxid, "txid"));
63  if (setTxIds.count(txid)) {
64  throw JSONRPCError(
66  std::string("Invalid parameter, duplicated txid: ") +
67  utxid.get_str());
68  }
69 
70  setTxIds.insert(txid);
71  oneTxId = txid;
72  }
73 
74  const CBlockIndex *pblockindex = nullptr;
75 
76  BlockHash hashBlock;
77  ChainstateManager &chainman = EnsureAnyChainman(request.context);
78  if (!request.params[1].isNull()) {
79  LOCK(cs_main);
80  hashBlock =
81  BlockHash(ParseHashV(request.params[1], "blockhash"));
82  pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
83  if (!pblockindex) {
85  "Block not found");
86  }
87  } else {
88  LOCK(cs_main);
89  Chainstate &active_chainstate = chainman.ActiveChainstate();
90  // Loop through txids and try to find which block they're in.
91  // Exit loop once a block is found.
92  for (const auto &txid : setTxIds) {
93  const Coin &coin =
94  AccessByTxid(active_chainstate.CoinsTip(), txid);
95  if (!coin.IsSpent()) {
96  pblockindex =
97  active_chainstate.m_chain[coin.GetHeight()];
98  break;
99  }
100  }
101  }
102 
103  // Allow txindex to catch up if we need to query it and before we
104  // acquire cs_main.
105  if (g_txindex && !pblockindex) {
106  g_txindex->BlockUntilSyncedToCurrentChain();
107  }
108 
109  const Consensus::Params &params =
110  config.GetChainParams().GetConsensus();
111 
112  if (pblockindex == nullptr) {
113  const CTransactionRef tx = GetTransaction(
114  /* block_index */ nullptr,
115  /* mempool */ nullptr, oneTxId, chainman.GetConsensus(),
116  hashBlock);
117  if (!tx || hashBlock.IsNull()) {
119  "Transaction not yet in block");
120  }
121 
122  LOCK(cs_main);
123  pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
124  if (!pblockindex) {
126  "Transaction index corrupt");
127  }
128  }
129 
130  CBlock block;
131  if (!ReadBlockFromDisk(block, pblockindex, params)) {
133  "Can't read block from disk");
134  }
135 
136  unsigned int ntxFound = 0;
137  for (const auto &tx : block.vtx) {
138  if (setTxIds.count(tx->GetId())) {
139  ntxFound++;
140  }
141  }
142 
143  if (ntxFound != setTxIds.size()) {
145  "Not all transactions found in specified or "
146  "retrieved block");
147  }
148 
150  CMerkleBlock mb(block, setTxIds);
151  ssMB << mb;
152  std::string strHex = HexStr(ssMB);
153  return strHex;
154  },
155  };
156 }
157 
159  return RPCHelpMan{
160  "verifytxoutproof",
161  "Verifies that a proof points to a transaction in a block, returning "
162  "the transaction it commits to\n"
163  "and throwing an RPC error if the block is not in our best chain\n",
164  {
166  "The hex-encoded proof generated by gettxoutproof"},
167  },
169  "",
170  "",
171  {
172  {RPCResult::Type::STR_HEX, "txid",
173  "The txid(s) which the proof commits to, or empty array "
174  "if the proof can not be validated."},
175  }},
176  RPCExamples{""},
177  [&](const RPCHelpMan &self, const Config &config,
178  const JSONRPCRequest &request) -> UniValue {
179  CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK,
181  CMerkleBlock merkleBlock;
182  ssMB >> merkleBlock;
183 
185 
186  std::vector<uint256> vMatch;
187  std::vector<size_t> vIndex;
188  if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) !=
189  merkleBlock.header.hashMerkleRoot) {
190  return res;
191  }
192 
193  ChainstateManager &chainman = EnsureAnyChainman(request.context);
194  LOCK(cs_main);
195 
196  const CBlockIndex *pindex = chainman.m_blockman.LookupBlockIndex(
197  merkleBlock.header.GetHash());
198  if (!pindex || !chainman.ActiveChain().Contains(pindex) ||
199  pindex->nTx == 0) {
201  "Block not found in chain");
202  }
203 
204  // Check if proof is valid, only add results if so
205  if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) {
206  for (const uint256 &hash : vMatch) {
207  res.push_back(hash.GetHex());
208  }
209  }
210 
211  return res;
212  },
213  };
214 }
215 
217  static const CRPCCommand commands[]{
218  // category actor (function)
219  // -------- ----------------
220  {"blockchain", gettxoutproof},
221  {"blockchain", verifytxoutproof},
222  };
223  for (const auto &c : commands) {
224  t.appendCommand(c.name, &c);
225  }
226 }
BlockHash GetHash() const
Definition: block.cpp:11
uint256 hashMerkleRoot
Definition: block.h:28
Definition: block.h:60
std::vector< CTransactionRef > vtx
Definition: block.h:63
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:26
unsigned int nTx
Number of transactions in this block.
Definition: blockindex.h:61
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:172
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:177
Used to create a Merkle proof (usually from a subset of transactions), which consists of a block head...
Definition: merkleblock.h:147
CBlockHeader header
Public only for unit testing.
Definition: merkleblock.h:150
CPartialMerkleTree txn
Definition: merkleblock.h:151
uint32_t GetNumTransactions() const
Get number of transactions the merkle proof is indicating for cross-reference with local blockchain k...
Definition: merkleblock.h:132
uint256 ExtractMatches(std::vector< uint256 > &vMatch, std::vector< size_t > &vnIndex)
Extract the matching txid's represented by this partial merkle tree and their respective indices with...
RPC command dispatcher.
Definition: server.h:183
void appendCommand(const std::string &name, const CRPCCommand *pcmd)
Appends a CRPCCommand to the dispatch table.
Definition: server.cpp:330
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:629
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:738
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:764
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1144
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1351
const Consensus::Params & GetConsensus() const
Definition: validation.h:1241
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1273
A UTXO entry.
Definition: coins.h:27
uint32_t GetHeight() const
Definition: coins.h:44
bool IsSpent() const
Definition: coins.h:46
Definition: config.h:17
const std::string & get_str() const
@ VARR
Definition: univalue.h:27
size_t size() const
Definition: univalue.h:80
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
const UniValue & get_array() const
bool IsNull() const
Definition: uint256.h:32
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
256-bit opaque blob.
Definition: uint256.h:129
const Coin & AccessByTxid(const CCoinsViewCache &view, const TxId &txid)
Utility function to find any unspent output with a given txid.
Definition: coins.cpp:331
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
CTransactionRef GetTransaction(const CBlockIndex *const block_index, const CTxMemPool *const mempool, const TxId &txid, const Consensus::Params &consensusParams, BlockHash &hashBlock)
Return transaction with a given txid.
bool ReadBlockFromDisk(CBlock &block, const FlatFilePos &pos, const Consensus::Params &params)
Functions for disk access for blocks.
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:57
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:46
@ RPC_INTERNAL_ERROR
Definition: protocol.h:33
@ RPC_INVALID_ADDRESS_OR_KEY
Invalid address or key.
Definition: protocol.h:42
std::vector< uint8_t > ParseHexV(const UniValue &v, std::string strName)
Definition: util.cpp:119
uint256 ParseHashV(const UniValue &v, std::string strName)
Utilities: convert hex-encoded values (throws error if not hex).
Definition: util.cpp:98
@ SER_NETWORK
Definition: serialize.h:152
ChainstateManager & EnsureAnyChainman(const std::any &context)
Definition: server_util.cpp:57
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
Parameters that influence chain consensus.
Definition: params.h:34
@ STR_HEX
Special type that is a STR with only hex chars.
@ OMITTED_NAMED_ARG
Optional arg that is a named argument and has a default value of null.
@ OMITTED
Optional argument with default value omitted because they are implicitly clear.
@ NO
Required arg.
@ STR_HEX
Special string with only hex chars.
A TxId is the identifier of a transaction.
Definition: txid.h:14
#define LOCK(cs)
Definition: sync.h:306
std::unique_ptr< TxIndex > g_txindex
The global transaction index, used in GetTransaction. May be null.
Definition: txindex.cpp:17
void RegisterTxoutProofRPCCommands(CRPCTable &t)
Definition: txoutproof.cpp:216
static RPCHelpMan gettxoutproof()
Definition: txoutproof.cpp:24
static RPCHelpMan verifytxoutproof()
Definition: txoutproof.cpp:158
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11