| 204 | } |
| 205 | |
| 206 | static UniValue gettxoutproof(const JSONRPCRequest& request) |
| 207 | { |
| 208 | if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) |
| 209 | throw std::runtime_error( |
| 210 | "gettxoutproof [\"txid\",...] ( blockhash )\n" |
| 211 | "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" |
| 212 | "\nNOTE: By default this function only works sometimes. This is when there is an\n" |
| 213 | "unspent output in the utxo for this transaction. To make it always work,\n" |
| 214 | "you need to maintain a transaction index, using the -txindex command line option or\n" |
| 215 | "specify the block in which the transaction is included manually (by blockhash).\n" |
| 216 | "\nArguments:\n" |
| 217 | "1. \"txids\" (string) A json array of txids to filter\n" |
| 218 | " [\n" |
| 219 | " \"txid\" (string) A transaction hash\n" |
| 220 | " ,...\n" |
| 221 | " ]\n" |
| 222 | "2. \"blockhash\" (string, optional) If specified, looks for txid in the block with this hash\n" |
| 223 | "\nResult:\n" |
| 224 | "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" |
| 225 | ); |
| 226 | |
| 227 | std::set<uint256> setTxids; |
| 228 | uint256 oneTxid; |
| 229 | UniValue txids = request.params[0].get_array(); |
| 230 | for (unsigned int idx = 0; idx < txids.size(); idx++) { |
| 231 | const UniValue& txid = txids[idx]; |
| 232 | if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) |
| 233 | throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid txid ")+txid.get_str()); |
| 234 | uint256 hash(uint256S(txid.get_str())); |
| 235 | if (setTxids.count(hash)) |
| 236 | throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ")+txid.get_str()); |
| 237 | setTxids.insert(hash); |
| 238 | oneTxid = hash; |
| 239 | } |
| 240 | |
| 241 | CBlockIndex* pblockindex = nullptr; |
| 242 | uint256 hashBlock; |
| 243 | if (!request.params[1].isNull()) { |
| 244 | LOCK(cs_main); |
| 245 | hashBlock = uint256S(request.params[1].get_str()); |
| 246 | pblockindex = LookupBlockIndex(hashBlock); |
| 247 | if (!pblockindex) { |
| 248 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); |
| 249 | } |
| 250 | } else { |
| 251 | LOCK(cs_main); |
| 252 | |
| 253 | // Loop through txids and try to find which block they're in. Exit loop once a block is found. |
| 254 | for (const auto& tx : setTxids) { |
| 255 | const Coin& coin = AccessByTxid(*pcoinsTip, tx); |
| 256 | if (!coin.IsSpent()) { |
| 257 | pblockindex = chainActive[coin.nHeight]; |
| 258 | break; |
| 259 | } |
| 260 | } |
| 261 | } |
| 262 | |
| 263 |
nothing calls this directly
no test coverage detected