| 309 | } |
| 310 | |
| 311 | static RPCHelpMan gettxoutproof() |
| 312 | { |
| 313 | return RPCHelpMan{"gettxoutproof", |
| 314 | "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" |
| 315 | "\nNOTE: By default this function only works sometimes. This is when there is an\n" |
| 316 | "unspent output in the utxo for this transaction. To make it always work,\n" |
| 317 | "you need to maintain a transaction index, using the -txindex command line option or\n" |
| 318 | "specify the block in which the transaction is included manually (by blockhash).\n", |
| 319 | { |
| 320 | {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The txids to filter", |
| 321 | { |
| 322 | {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"}, |
| 323 | }, |
| 324 | }, |
| 325 | {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"}, |
| 326 | }, |
| 327 | RPCResult{ |
| 328 | RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof." |
| 329 | }, |
| 330 | RPCExamples{""}, |
| 331 | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
| 332 | { |
| 333 | std::set<uint256> setTxids; |
| 334 | UniValue txids = request.params[0].get_array(); |
| 335 | if (txids.empty()) { |
| 336 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter 'txids' cannot be empty"); |
| 337 | } |
| 338 | for (unsigned int idx = 0; idx < txids.size(); idx++) { |
| 339 | auto ret = setTxids.insert(ParseHashV(txids[idx], "txid")); |
| 340 | if (!ret.second) { |
| 341 | throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + txids[idx].get_str()); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | CBlockIndex* pblockindex = nullptr; |
| 346 | uint256 hashBlock; |
| 347 | ChainstateManager& chainman = EnsureAnyChainman(request.context); |
| 348 | if (!request.params[1].isNull()) { |
| 349 | LOCK(cs_main); |
| 350 | hashBlock = ParseHashV(request.params[1], "blockhash"); |
| 351 | pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock); |
| 352 | if (!pblockindex) { |
| 353 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); |
| 354 | } |
| 355 | } else { |
| 356 | LOCK(cs_main); |
| 357 | CChainState& active_chainstate = chainman.ActiveChainstate(); |
| 358 | |
| 359 | // Loop through txids and try to find which block they're in. Exit loop once a block is found. |
| 360 | for (const auto& tx : setTxids) { |
| 361 | const Coin& coin = AccessByTxid(active_chainstate.CoinsTip(), tx); |
| 362 | if (!coin.IsSpent()) { |
| 363 | pblockindex = active_chainstate.m_chain[coin.nHeight]; |
| 364 | break; |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 |
nothing calls this directly
no test coverage detected