From ab02a43357c454f7b8b9e382a2568227e13c1319 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 14 Jun 2017 15:36:56 +0200 Subject: [PATCH] Merge #9738: gettxoutproof() should return consistent result 6294f32 gettxoutproof() should return consistent result (John Newbery) Tree-SHA512: 1c36f78ea07a3bdde09e9494207b4372d54bcd94ed2d56e339e78281f6693e26a93e4c3123453d5c0f6e994d0069d5a1c806786c4af71864f87ea4841611c379 --- src/rpc/rawtransaction.cpp | 12 ++++++++---- test/functional/merkle_blocks.py | 25 +++++++++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 676a9521b7..318b601c1b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -265,9 +265,13 @@ UniValue gettxoutproof(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); pblockindex = mapBlockIndex[hashBlock]; } else { - const Coin& coin = AccessByTxid(*pcoinsTip, oneTxid); - if (!coin.IsSpent() && coin.nHeight > 0 && coin.nHeight <= chainActive.Height()) { - pblockindex = chainActive[coin.nHeight]; + // Loop through txids and try to find which block they're in. Exit loop once a block is found. + for (const auto& tx : setTxids) { + const Coin& coin = AccessByTxid(*pcoinsTip, tx); + if (!coin.IsSpent()) { + pblockindex = chainActive[coin.nHeight]; + break; + } } } @@ -290,7 +294,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request) if (setTxids.count(tx->GetHash())) ntxFound++; if (ntxFound != setTxids.size()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block"); CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock mb(block, setTxids); diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py index 564a4b5b01..d9d3e17f94 100755 --- a/test/functional/merkle_blocks.py +++ b/test/functional/merkle_blocks.py @@ -40,7 +40,8 @@ class MerkleBlockTest(BitcoinTestFramework): txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx1)["hex"]) tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 500 - tx_fee}) txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"]) - assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [txid1]) + # This will raise an exception because the transaction is not yet in a block + assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) @@ -57,7 +58,7 @@ class MerkleBlockTest(BitcoinTestFramework): txin_spent = self.nodes[1].listunspent(1).pop() tx3 = self.nodes[1].createrawtransaction([txin_spent], {self.nodes[0].getnewaddress(): 500 - tx_fee*2}) - self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransaction(tx3)["hex"]) + txid3 = self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransaction(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() @@ -66,17 +67,21 @@ class MerkleBlockTest(BitcoinTestFramework): # We can't find the block from a fully-spent tx # Doesn't apply to Dash Core - we have txindex always on - # assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) - # ...but we can if we specify the block + # assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent]) + # We can get the proof if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) - # ...or if the first tx is not fully-spent + # We can't get the proof if we specify a non-existent block + assert_raises_jsonrpc(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000") + # We can get the proof if the transaction is unspent assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent]) - try: - assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), txlist) - except JSONRPCException: - assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid2, txid1])), txlist) - # ...or if we have a -txindex + # We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter. + assert_equal(sorted(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2]))), sorted(txlist)) + assert_equal(sorted(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid2, txid1]))), sorted(txlist)) + # We can always get a proof if we have a -txindex assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent]) + # We can't get a proof if we specify transactions from different blocks + assert_raises_jsonrpc(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3]) + if __name__ == '__main__': MerkleBlockTest().main()