Skip to content

Conversation

jonatack
Copy link
Member

@jonatack jonatack commented Jul 13, 2021

Following up on #22383 (review), this pull adds missing src/node/transaction::GetTransaction() test coverage for combinations of -txindex and blockhash and does some refactoring of the test file.

@fanquake fanquake added the Tests label Jul 13, 2021
@jonatack
Copy link
Member Author

jonatack commented Jul 13, 2021

Test output after these changes. The slow legacy multisig tests are placed at the end.

$ test/functional/rpc_rawtransaction.py 
2021-08-31T20:07:10.312000Z TestFramework (INFO): Initializing test directory /tmp/bitcoin_func_test_uf3csrh5
2021-08-31T20:07:11.886000Z TestFramework (INFO): Prepare some coins for multiple *rawtransaction commands
2021-08-31T20:07:20.276000Z TestFramework (INFO): Test getrawtransaction with -txindex
2021-08-31T20:07:20.306000Z TestFramework (INFO): Test getrawtransaction without -txindex
2021-08-31T20:07:21.601000Z TestFramework (INFO): Test getrawtransaction with -txindex, with blockhash
2021-08-31T20:07:21.605000Z TestFramework (INFO): Test getrawtransaction with -txindex, without blockhash: 'in_active_chain' should be absent
2021-08-31T20:07:21.655000Z TestFramework (INFO): Test getrawtransaction without -txindex, with blockhash
2021-08-31T20:07:21.659000Z TestFramework (INFO): Test getrawtransaction without -txindex, without blockhash: expect the call to raise
2021-08-31T20:07:21.704000Z TestFramework (INFO): Test getrawtransaction on genesis block coinbase returns an error
2021-08-31T20:07:21.712000Z TestFramework (INFO): Test createrawtransaction
2021-08-31T20:07:21.944000Z TestFramework (INFO): Test signrawtransactionwithwallet with missing prevtx info (bech32)
2021-08-31T20:07:22.021000Z TestFramework (INFO): Test signrawtransactionwithwallet with missing prevtx info (p2sh-segwit)
2021-08-31T20:07:22.106000Z TestFramework (INFO): Test signrawtransactionwithwallet with missing prevtx info (legacy)
2021-08-31T20:07:22.175000Z TestFramework (INFO): Test sendrawtransaction with missing input
2021-08-31T20:07:22.205000Z TestFramework (INFO): Test sendrawtransaction/testmempoolaccept with maxfeerate
2021-08-31T20:07:23.777000Z TestFramework (INFO): Test sendrawtransaction/testmempoolaccept with tx already in the chain
2021-08-31T20:07:23.858000Z TestFramework (INFO): Test decoderawtransaction
2021-08-31T20:07:23.886000Z TestFramework (INFO): Test transaction version numbers
2021-08-31T20:07:23.894000Z TestFramework (INFO): Test raw multisig transactions (legacy)
2021-08-31T20:07:30.595000Z TestFramework (INFO): Stopping nodes
2021-08-31T20:07:30.858000Z TestFramework (INFO): Cleaning up /tmp/bitcoin_func_test_uf3csrh5 on exit
2021-08-31T20:07:30.858000Z TestFramework (INFO): Tests successful

@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 13, 2021

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #22788 (scripted-diff: Use generate* from TestFramework by MarcoFalke)
  • #19831 (test: Check that decoderawtransaction heuristic may fail by MarcoFalke)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@mjdietzx
Copy link
Contributor

ACK 993189b very nicely done!

@jonatack jonatack force-pushed the improve-gettransaction-test-coverage branch from 993189b to d27edf1 Compare July 15, 2021 08:17
@jonatack
Copy link
Member Author

Thanks @mjdietzx! Rebased to master following the merge of #22447, dropping the first two commits; no other change.

git range-diff 97153a7 993189b d27edf1

Copy link
Contributor

@lsilva01 lsilva01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested ACK d27edf1 on Ubuntu 20.04

Copy link
Contributor

@rajarshimaitra rajarshimaitra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK d27edf1.

verified that the test passes with #22383 changes.

Below are few comments regarding test coverage and arrangements.

@practicalswift
Copy link
Contributor

Concept ACK

@jonatack
Copy link
Member Author

jonatack commented Aug 8, 2021

Rebased due to #22510 and updated with the excellent review feedback from @kiminuo and @rajarshimaitra (thanks!)

Commit-by-commit changes (re-pushed a second time for #22437 (comment)):

git range-diff db94d74 d27edf1 7f7e64e

Thank you @mjdietzx, @lsilva01, and @rajarshimaitra for the ACKs. Would you mind re-ACKing?

@jonatack jonatack force-pushed the improve-gettransaction-test-coverage branch from afb4eab to 7f7e64e Compare August 8, 2021 16:30
Copy link
Contributor

@rajarshimaitra rajarshimaitra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jonatack for considering the comments.

Re tested ACK afb4eab

It might not be a good place to discuss test approaches here, but I have the following observations

  • I am finding that getrawtransaction_tests setup can be simplified a bit. Instead of 3 transactions (all in the chain) we can do the test with just one. All we need is a tx in a block and its id. So if we simply start the test like this
       test_tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
       test_tx_hex = self.nodes[2].getrawtransaction(test_tx)
       block1, block2 = self.nodes[2].generate(2)
       self.sync_peers()

this will give us everything we want to do the assertions. This I feel will simplify the testing logic and would make the test easy
to reason about.

  • The #22383 changes behaviour that if txindex is on, the blockhash searching won't take place even if it's provided. It seems to me that this particular behaviour is not being tested. I am also not sure what can be a possible approach to test this. But I feel this needs to be covered, as it's a performance boost, and we don't want future PR to accidentally change this.

Would like to have your thoughts on the above. It's not necessary to have those changes in this PR itself.

Below is a minor redundancy I found.

@jonatack
Copy link
Member Author

jonatack commented Aug 8, 2021

@rajarshimaitra I agree, bringing together the various related tests shows that we can simplify them. I'll look at integrating the following diff based on your suggestion, which works for me, into the changes.

code diff

diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 84210d3a03..cc6324d9fc 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -101,19 +101,9 @@ class RawTransactionsTest(BitcoinTestFramework):
             self.raw_multisig_transaction_legacy_tests()
 
     def getrawtransaction_tests(self):
-        addr = self.nodes[1].getnewaddress()
-        txid = self.nodes[0].sendtoaddress(addr, 10)
-        self.generate_and_sync(node=0, blocks=1)
-        vout = find_vout_for_address(self.nodes[1], txid, addr)
-        rawTx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): 9.999})
-        rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx)
-        txid2 = self.nodes[1].sendrawtransaction(rawTxSigned['hex'])
-        self.generate_and_sync(node=0, blocks=1)
-
         # Make a tx by sending, then generate 2 blocks; block1 has the tx in it
-        txid3 = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
+        tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
+        hex = self.nodes[2].getrawtransaction(tx)
         block1, block2 = self.nodes[2].generate(2)
-        self.sync_peers()
 
         err_msg = (
             "No such mempool transaction. Use -txindex or provide a block hash to enable"
@@ -134,47 +124,44 @@ class RawTransactionsTest(BitcoinTestFramework):
             if n == 0 or n == 5:
                 # with -txindex
                 for verbose in [None, 0, False]:
-                    assert_equal(self.nodes[n].getrawtransaction(txid2, verbose), rawTxSigned['hex'])
+                    assert_equal(self.nodes[n].getrawtransaction(tx, verbose), hex)
                 for verbose in [1, True]:
-                    gottx1 = self.nodes[n].getrawtransaction(txid2, verbose)
-                    assert_equal(gottx1['hex'], rawTxSigned['hex'])
-                    assert 'in_active_chain' not in gottx1.keys()
-                    gottx2 = self.nodes[n].getrawtransaction(txid=txid3, verbose=verbose)
-                    assert_equal(gottx2['txid'], txid3)
-                    assert 'in_active_chain' not in gottx2.keys()
+                    gottx = self.nodes[n].getrawtransaction(tx, verbose)
+                    assert_equal(gottx['hex'], hex)
+                    assert 'in_active_chain' not in gottx.keys()
             else:
                 # without -txindex
                 for verbose in [None, 0, False, 1, True]:
-                    assert_raises_rpc_error(-5, err_msg, self.nodes[n].getrawtransaction, txid2, verbose)
+                    assert_raises_rpc_error(-5, err_msg, self.nodes[n].getrawtransaction, tx, verbose)
 
             # 2. invalid parameters - supply txid and invalid boolean values (strings) for verbose
             for value in ["True", "False"]:
-                assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txid=txid2, verbose=value)
+                assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txid=tx, verbose=value)
 
             # 3. invalid parameters - supply txid and empty array
-            assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txid2, [])
+            assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, tx, [])
 
             # 4. invalid parameters - supply txid and empty dict
-            assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txid2, {})
+            assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, tx, {})
 
             # 5. with block hash
             # We should be able to get the raw transaction by providing the correct block
-            gottx = self.nodes[n].getrawtransaction(txid=txid3, verbose=True, blockhash=block1)
-            assert_equal(gottx['txid'], txid3)
+            gottx = self.nodes[n].getrawtransaction(txid=tx, verbose=True, blockhash=block1)
+            assert_equal(gottx['txid'], tx)
             assert_equal(gottx['in_active_chain'], True)
             # We should not get the tx if we provide an unrelated block
-            assert_raises_rpc_error(-5, "No such transaction found", self.nodes[n].getrawtransaction, txid=txid3, blockhash=block2)
+            assert_raises_rpc_error(-5, "No such transaction found", self.nodes[n].getrawtransaction, txid=tx, blockhash=block2)
             # An invalid block hash should raise the correct errors
-            assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[n].getrawtransaction, txid=txid3, blockhash=True)
-            assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[n].getrawtransaction, txid=txid3, blockhash="foobar")
-            assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[n].getrawtransaction, txid=txid3, blockhash="abcd1234")
+            assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[n].getrawtransaction, txid=tx, blockhash=True)
+            assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[n].getrawtransaction, txid=tx, blockhash="foobar")
+            assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[n].getrawtransaction, txid=tx, blockhash="abcd1234")
             foo = "ZZZ0000000000000000000000000000000000000000000000000000000000000"
-            assert_raises_rpc_error(-8, f"parameter 3 must be hexadecimal string (not '{foo}')", self.nodes[n].getrawtransaction, txid=txid3, blockhash=foo)
+            assert_raises_rpc_error(-8, f"parameter 3 must be hexadecimal string (not '{foo}')", self.nodes[n].getrawtransaction, txid=tx, blockhash=foo)
             bar = "0000000000000000000000000000000000000000000000000000000000000000"
-            assert_raises_rpc_error(-5, "Block hash not found", self.nodes[n].getrawtransaction, txid=txid3, blockhash=bar)
+            assert_raises_rpc_error(-5, "Block hash not found", self.nodes[n].getrawtransaction, txid=tx, blockhash=bar)
             # Undo the blocks and verify that "in_active_chain" is false.
             self.nodes[n].invalidateblock(block1)
-            gottx = self.nodes[n].getrawtransaction(txid=txid3, verbose=True, blockhash=block1)
+            gottx = self.nodes[n].getrawtransaction(txid=tx, verbose=True, blockhash=block1)
             assert_equal(gottx['in_active_chain'], False)
             self.nodes[n].reconsiderblock(block1)
             assert_equal(self.nodes[n].getbestblockhash(), block2)

It if txindex is on, the blockhash searching won't take place even if it's provided. It seems to me that this particular behaviour is not being tested. I am also not sure what can be a possible approach to test this.

At first glance I don't see a straightforward way to test which code path is taken in that case with the current code, as there is no observable difference in behavior other than hopefully in performance, for which a benchmark could be added.

@jonatack
Copy link
Member Author

jonatack commented Aug 8, 2021

@rajarshimaitra I've appended a commit with you as the author. Let me know if the name and email codeShark149 <rajarshi149@gmail.com> are correct.

@jonatack jonatack force-pushed the improve-gettransaction-test-coverage branch from 3bd7ead to c2d7995 Compare August 8, 2021 18:14
@fanquake
Copy link
Member

fanquake commented Aug 9, 2021

It could be a good to reorder some of these commits so we're not making one change, then changing the same lines again straight after. For example, in 10a3db0 you rename variables i.e tx, txId to txid2, txid3 etc. However in the following commit (c2d7995), a bunch of those end up being renamed again, i.e txid3 and txid2 back to tx.

@jonatack
Copy link
Member Author

Thanks everyone for the reviews! It would be nice for this to be merged before any other change touching this file invalidates all the review. One can hope :)

@jonatack jonatack force-pushed the improve-gettransaction-test-coverage branch from f0aacf0 to d426cab Compare August 31, 2021 14:12
@jonatack
Copy link
Member Author

Dropped the commits after d426cab, no other change to not invalidate review. Can continue with the other improvements (test de-duplication, in-mempool txn tests, setup simplification, variable naming cleanup, and others) in a follow-up.

@jonatack jonatack force-pushed the improve-gettransaction-test-coverage branch from d426cab to adfade6 Compare August 31, 2021 19:42
@jonatack
Copy link
Member Author

jonatack commented Aug 31, 2021

Squashed the first two commits per review feedback and made some minor improvements per git diff d426cab 387355b.

@jonatack jonatack force-pushed the improve-gettransaction-test-coverage branch from adfade6 to 387355b Compare August 31, 2021 20:07
@mjdietzx
Copy link
Contributor

reACK 387355b

@bitcoin bitcoin deleted a comment from jaysonmald35 Sep 1, 2021
@josibake
Copy link
Member

josibake commented Sep 1, 2021

reACK 387355b

Copy link
Member

@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. Thanks.

Approach ACK 387355b 🔆

Show signature and timestamp

Signature:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Approach ACK 387355bb9482a09c1fc9b137bea56745a93b7dfd 🔆
-----BEGIN PGP SIGNATURE-----

iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUjpnQv/d+jQxCI/GaFHTfHIOmYvfjLAIVoaPnCfJYPoRavcXA92Ov2DiktzzN2j
j8s0Y7Jy86T33aBsbFnGBxM5hWG/WpaceaR7Ga3ixRMQce8TRB9Hng+ab9SuhL7I
eQUdaCAYCDr8pRdY3lmcOOJGzSPNKdiuf2cfFwyDL287VCXrn4JNCTOHZBfjknBu
8aTzGoJXkbbCtxQ26+aNFn4otQrQES9x8VkfrCUbGrUG9wrq64lbhqeoKq3rg6cd
ZbcxL6B8RLVBGiGGVyK4pn0P/sQmoCuND+1fEpUOT7TR7EFmI54Ah3xg3ycsvwx6
WZv+iDh+Zgv/TqTmMvklCHzdxanzxSbnjZ8hu8xiOs3ZVC6r98yYW0Z0QBSx+PN6
WizL0l1tCna5qnRl3ySta74GTOrtFUSihQq9jl+3AWV3Mo3c3Twuz8BCrCXoY+/J
xZEtkIwscpRNKC9nlGzKwItI59+s+B7E3AgmF9gDmOddQ6ZDSwrwrVOrMwuhO9aw
lSJFPNLT
=NEYX
-----END PGP SIGNATURE-----

Timestamp of file with hash 2d816e7fc4d23afbf4d13cf22ed4e17e67a0d483e53eeb2099251d3af22c67e7 -

@maflcko maflcko merged commit 7e75400 into bitcoin:master Sep 1, 2021
@jonatack jonatack deleted the improve-gettransaction-test-coverage branch September 1, 2021 17:39
vout = find_vout_for_address(self.nodes[1], txid, addr)
rawTx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): 9.999})
rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx)
txId = self.nodes[1].sendrawtransaction(rawTxSigned['hex'])
Copy link
Member

@maflcko maflcko Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

                                   Traceback (most recent call last):
                                     File "/home/marco/workspace/btc_bitcoin_core/test/functional/test_framework/test_framework.py", line 132, in main
                                       self.run_test()
                                     File "/home/marco/workspace/btc_bitcoin_core/test/functional/rpc_rawtransaction.py", line 84, in run_test
                                       self.getrawtransaction_tests()
                                     File "/home/marco/workspace/btc_bitcoin_core/test/functional/rpc_rawtransaction.py", line 107, in getrawtransaction_tests
                                       assert_equal(self.nodes[n].getrawtransaction(txId), rawTxSigned['hex'])
                                     File "/home/marco/workspace/btc_bitcoin_core/test/functional/test_framework/coverage.py", line 49, in __call__
                                       return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
                                     File "/home/marco/workspace/btc_bitcoin_core/test/functional/test_framework/authproxy.py", line 144, in __call__
                                       raise JSONRPCException(response['error'], status)
                                   test_framework.authproxy.JSONRPCException: No such mempool transaction. Use -txindex or provide a block hash to enable blockchain transaction queries. Use gettransaction for wallet transactions. (-5)

not sure how this could happen, though...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the test isn't testing anything, as the tx is never included in a block.

If the tx is included, the test will fail:

diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 96691b2686..b648012413 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -99,7 +99,7 @@ class RawTransactionsTest(BitcoinTestFramework):
         rawTx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): 9.999})
         rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx)
         txId = self.nodes[1].sendrawtransaction(rawTxSigned['hex'])
-        self.generate(self.nodes[0], 1)
+        self.generateblock(self.nodes[0], output=self.nodes[0].getnewaddress(), transactions=[rawTxSigned['hex']])
 
         for n in [0, 3]:
             self.log.info(f"Test getrawtransaction {'with' if n == 0 else 'without'} -txindex")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, was working on the part 2 follow-up to this, will have a look.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.