|
9 | 9 | #include "init.h"
|
10 | 10 | #include "keystore.h"
|
11 | 11 | #include "main.h"
|
| 12 | +#include "merkleblock.h" |
12 | 13 | #include "net.h"
|
13 | 14 | #include "rpcserver.h"
|
14 | 15 | #include "script/script.h"
|
@@ -193,6 +194,119 @@ Value getrawtransaction(const Array& params, bool fHelp)
|
193 | 194 | return result;
|
194 | 195 | }
|
195 | 196 |
|
| 197 | +Value gettxoutproof(const Array& params, bool fHelp) |
| 198 | +{ |
| 199 | + if (fHelp || (params.size() != 1 && params.size() != 2)) |
| 200 | + throw runtime_error( |
| 201 | + "gettxoutproof [\"txid\",...] ( blockhash )\n" |
| 202 | + "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" |
| 203 | + "\nNOTE: By default this function only works sometimes. This is when there is an\n" |
| 204 | + "unspent output in the utxo for this transaction. To make it always work,\n" |
| 205 | + "you need to maintain a transaction index, using the -txindex command line option or\n" |
| 206 | + "specify the block in which the transaction is included in manually (by blockhash).\n" |
| 207 | + "\nReturn the raw transaction data.\n" |
| 208 | + "\nArguments:\n" |
| 209 | + "1. \"txids\" (string) A json array of txids to filter\n" |
| 210 | + " [\n" |
| 211 | + " \"txid\" (string) A transaction hash\n" |
| 212 | + " ,...\n" |
| 213 | + " ]\n" |
| 214 | + "2. \"block hash\" (string, optional) If specified, looks for txid in the block with this hash\n" |
| 215 | + "\nResult:\n" |
| 216 | + "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" |
| 217 | + ); |
| 218 | + |
| 219 | + set<uint256> setTxids; |
| 220 | + uint256 oneTxid; |
| 221 | + Array txids = params[0].get_array(); |
| 222 | + BOOST_FOREACH(Value& txid, txids) { |
| 223 | + if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) |
| 224 | + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); |
| 225 | + uint256 hash(uint256S(txid.get_str())); |
| 226 | + if (setTxids.count(hash)) |
| 227 | + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); |
| 228 | + setTxids.insert(hash); |
| 229 | + oneTxid = hash; |
| 230 | + } |
| 231 | + |
| 232 | + LOCK(cs_main); |
| 233 | + |
| 234 | + CBlockIndex* pblockindex = NULL; |
| 235 | + |
| 236 | + uint256 hashBlock; |
| 237 | + if (params.size() > 1) |
| 238 | + { |
| 239 | + hashBlock = uint256S(params[1].get_str()); |
| 240 | + if (!mapBlockIndex.count(hashBlock)) |
| 241 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); |
| 242 | + pblockindex = mapBlockIndex[hashBlock]; |
| 243 | + } else { |
| 244 | + CCoins coins; |
| 245 | + if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) |
| 246 | + pblockindex = chainActive[coins.nHeight]; |
| 247 | + } |
| 248 | + |
| 249 | + if (pblockindex == NULL) |
| 250 | + { |
| 251 | + CTransaction tx; |
| 252 | + if (!GetTransaction(oneTxid, tx, hashBlock, false) || hashBlock.IsNull()) |
| 253 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); |
| 254 | + if (!mapBlockIndex.count(hashBlock)) |
| 255 | + throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); |
| 256 | + pblockindex = mapBlockIndex[hashBlock]; |
| 257 | + } |
| 258 | + |
| 259 | + CBlock block; |
| 260 | + if(!ReadBlockFromDisk(block, pblockindex)) |
| 261 | + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); |
| 262 | + |
| 263 | + unsigned int ntxFound = 0; |
| 264 | + BOOST_FOREACH(const CTransaction&tx, block.vtx) |
| 265 | + if (setTxids.count(tx.GetHash())) |
| 266 | + ntxFound++; |
| 267 | + if (ntxFound != setTxids.size()) |
| 268 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); |
| 269 | + |
| 270 | + CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); |
| 271 | + CMerkleBlock mb(block, setTxids); |
| 272 | + ssMB << mb; |
| 273 | + std::string strHex = HexStr(ssMB.begin(), ssMB.end()); |
| 274 | + return strHex; |
| 275 | +} |
| 276 | + |
| 277 | +Value verifytxoutproof(const Array& params, bool fHelp) |
| 278 | +{ |
| 279 | + if (fHelp || params.size() != 1) |
| 280 | + throw runtime_error( |
| 281 | + "verifytxoutproof \"proof\"\n" |
| 282 | + "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" |
| 283 | + "and throwing an RPC error if the block is not in our best chain\n" |
| 284 | + "\nArguments:\n" |
| 285 | + "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" |
| 286 | + "\nResult:\n" |
| 287 | + "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" |
| 288 | + ); |
| 289 | + |
| 290 | + CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); |
| 291 | + CMerkleBlock merkleBlock; |
| 292 | + ssMB >> merkleBlock; |
| 293 | + |
| 294 | + Array res; |
| 295 | + |
| 296 | + vector<uint256> vMatch; |
| 297 | + if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot) |
| 298 | + return res; |
| 299 | + |
| 300 | + LOCK(cs_main); |
| 301 | + |
| 302 | + if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) |
| 303 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); |
| 304 | + |
| 305 | + BOOST_FOREACH(const uint256& hash, vMatch) |
| 306 | + res.push_back(hash.GetHex()); |
| 307 | + return res; |
| 308 | +} |
| 309 | + |
196 | 310 | Value createrawtransaction(const Array& params, bool fHelp)
|
197 | 311 | {
|
198 | 312 | if (fHelp || params.size() != 2)
|
|
0 commit comments