| 21 | using node::GetTransaction; |
| 22 | |
| 23 | static RPCMethod gettxoutproof() |
| 24 | { |
| 25 | return RPCMethod{ |
| 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 RPCMethod& 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 | } |
nothing calls this directly
no test coverage detected