test, refactor: add GetTransaction() coverage, improve rpc_rawtransaction #22437

pull jonatack wants to merge 9 commits into bitcoin:master from jonatack:improve-gettransaction-test-coverage changing 1 files +313 −301
  1. jonatack commented at 1:23 PM on July 13, 2021: member

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

  2. fanquake added the label Tests on Jul 13, 2021
  3. jonatack commented at 1:46 PM on July 13, 2021: member

    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
    
  4. jonatack force-pushed on Jul 13, 2021
  5. DrahtBot commented at 7:10 PM on July 13, 2021: member

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--174a7506f384e20aa4161008e828411d-->

    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.

  6. jonatack force-pushed on Jul 14, 2021
  7. jonatack force-pushed on Jul 14, 2021
  8. mjdietzx commented at 7:46 PM on July 14, 2021: contributor

    ACK 993189b1fe39cfc29e960ea3a20092309001fa8f very nicely done!

  9. jonatack force-pushed on Jul 15, 2021
  10. jonatack commented at 8:20 AM on July 15, 2021: member

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

    git range-diff 97153a7 993189b d27edf1

  11. in test/functional/rpc_rawtransaction.py:148 in d27edf1d85 outdated
     156 | +            else:
     157 | +                # without -txindex
     158 | +                for verbose in [None, 0, False, 1, True]:
     159 | +                    assert_raises_rpc_error(-5, err_msg, self.nodes[n].getrawtransaction, txid, verbose)
     160 | +
     161 | +            # 2. invalid parameters - supply txid and string "Flase"
    


    kiminuo commented at 8:42 PM on July 20, 2021:

    Maybe:

                # 2. invalid parameters - supply txid and string "Flase" (intentionally misspelled "False").
    

    ?


    jonatack commented at 3:52 PM on August 7, 2021:

    I agree that it's unclear. Changed to the following in b0bf8c996034a4:

    -            # 6. invalid parameters - supply txid and string "Flase"
    -            assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txId, "Flase")
    +            # 6. 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=txId, verbose=value)
    
  12. in test/functional/rpc_rawtransaction.py:145 in d27edf1d85 outdated
     150 | +                for verbose in [1, True]:
     151 | +                    # We only check the "hex" field of the output so we don't need to update
     152 | +                    # this test every time the output format changes.
     153 | +                    gottx = self.nodes[n].getrawtransaction(txId, verbose)['hex']
     154 | +                    assert_equal(gottx, rawTxSigned['hex'])
     155 | +                    assert 'in_active_chain' not in gottx
    


    kiminuo commented at 8:47 PM on July 20, 2021:

    This is suspicious to me. Isn't this always true given that gottx is in hex?


    jonatack commented at 10:57 AM on August 8, 2021:

    This assert tests that the in_active_chain field is absent as expected when the blockhash argument is not passed...see src/rpc/rawtransaction.cpp::getrawtransaction() or bitcoin-cli help getrawtransaction


    kiminuo commented at 12:38 PM on August 8, 2021:

    Just to make myself more clear: L140 ends with ['hex'] so presumably gottx is a string, so testing on L142 whether gottx does not contain in_active_chain seems to be always true.

    But maybe I just didn't get your reply. Anyway, if I got it wrong, feel free to just ignore my comment.


    jonatack commented at 3:39 PM on August 8, 2021:

    Thank you for re-explaining. You are right! Fixing. Thank you!


    jonatack commented at 4:33 PM on August 8, 2021:

    Updated commit 4e83843f03dad710a0fad7a706e2618a7a69a6b3 to fix. Thanks @kiminuo!

  13. in test/functional/rpc_rawtransaction.py:78 in d27edf1d85 outdated
      73 | @@ -69,24 +74,129 @@ def setup_network(self):
      74 |          super().setup_network()
      75 |          self.connect_nodes(0, 2)
      76 |  
      77 | +    def sync_peers(self):
      78 | +        self.sync_mempools(self.nodes[0:3])
    


    kiminuo commented at 9:01 PM on July 20, 2021:

    Question: Is this intentionally only on 4 nodes instead of 6? If it is, maybe sync_peers should be called slightly differently not to give the wrong impression.


    jonatack commented at 3:37 PM on August 7, 2021:

    Question: Is this intentionally only on 4 nodes instead of 6?

    Yes, as only nodes 0 to 3 have a mempool. The last two nodes are -blocksonly and sync_mempools() won't work on them. Added a comment in efe13c3b77 to clarify this.

    -        self.sync_mempools()
    +        self.sync_mempools(self.nodes[0:3])  # nodes 0 to 3 have a mempool
    
  14. in test/functional/rpc_rawtransaction.py:105 in d27edf1d85 outdated
     119 | +        txid = self.nodes[0].sendtoaddress(addr, 10)
     120 | +        self.generate_and_sync(node=0, blocks=1)
     121 | +        vout = find_vout_for_address(self.nodes[1], txid, addr)
     122 | +        rawTx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): 9.999})
     123 | +        rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx)
     124 | +        txId = self.nodes[1].sendrawtransaction(rawTxSigned['hex'])
    


    kiminuo commented at 9:11 PM on July 20, 2021:

    Not originally your code, but variables named txid and txId are very easy to confuse, I think.


    rajarshimaitra commented at 2:52 PM on July 23, 2021:

    +1. Maybe something like txid1, txid2?


    jonatack commented at 7:29 PM on August 7, 2021:

    I initially planned to respond that this was out of scope, then saw this very issue had tripped me up (https://github.com/bitcoin/bitcoin/pull/22437#discussion_r684653116). Agree! Done in 10a3db049ce2858 when the name is reused or reassigned in the same test function. Doing this also revealed an unused txId assignment; fixed.

  15. in test/functional/rpc_rawtransaction.py:146 in d27edf1d85 outdated
     154 | +                    assert_equal(gottx, rawTxSigned['hex'])
     155 | +                    assert 'in_active_chain' not in gottx
     156 | +            else:
     157 | +                # without -txindex
     158 | +                for verbose in [None, 0, False, 1, True]:
     159 | +                    assert_raises_rpc_error(-5, err_msg, self.nodes[n].getrawtransaction, txid, verbose)
    


    kiminuo commented at 9:14 PM on July 20, 2021:

    I would expect txId instead of txid here. Or can you explain this please?


    jonatack commented at 4:40 PM on August 7, 2021:

    Good catch! This illustrates why using txid and txId variable names in the same test isn't ideal. Fixed in commit "Test src/validation::GetTransaction() with -blocksonly".

  16. lsilva01 approved
  17. lsilva01 commented at 11:10 PM on July 22, 2021: contributor
  18. in test/functional/rpc_rawtransaction.py:88 in d27edf1d85 outdated
      96 | -        self.nodes[0].generate(5)
      97 | -        self.sync_all()
      98 | -
      99 | -        self.log.info('Test getrawtransaction on genesis block coinbase returns an error')
     100 | +        self.log.info("Prepare some coins for multiple *rawtransaction commands")
     101 | +        self.generate_and_sync(node=2, blocks=1)
    


    rajarshimaitra commented at 2:38 PM on July 23, 2021:

    Is it necessary here to have node[2] create 1 block first?


    jonatack commented at 8:02 PM on August 7, 2021:

    Well spotted! Thanks to the transactions in the next lines, it's not needed and might be an example of the accumulated layers of change in this test file. Removed in commit 8f5c6ba69000b with an explanation in the commit message.

  19. in test/functional/rpc_rawtransaction.py:157 in d27edf1d85 outdated
     165 | +            assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txId, [])
     166 | +
     167 | +            # 4. invalid parameters - supply txid and empty dict
     168 | +            assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txId, {})
     169 | +
     170 | +            # 5. invalid parameters - sequence out of range
    


    rajarshimaitra commented at 2:56 PM on July 23, 2021:

    This doesn't seem like getrawtransaction() tests. Maybe test 5 and 6 should be moved into createrawtransaction() test group?


    jonatack commented at 7:24 PM on August 7, 2021:

    Good idea! Done in a3d8f790633b89a.

  20. in test/functional/rpc_rawtransaction.py:111 in d27edf1d85 outdated
     120 | +        self.generate_and_sync(node=0, blocks=1)
     121 | +        vout = find_vout_for_address(self.nodes[1], txid, addr)
     122 | +        rawTx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): 9.999})
     123 | +        rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx)
     124 | +        txId = self.nodes[1].sendrawtransaction(rawTxSigned['hex'])
     125 | +        self.generate_and_sync(node=0, blocks=1)
    


    rajarshimaitra commented at 3:00 PM on July 23, 2021:

    I am not sure whether having two txid and txId transactions helps here, given both of them are confirmed. Because of that we are also not getting a test for only mempool transactions.

    So maybe we can have a txid confirmed in a block, and then have txId in the mempool to check for both the scenario?

    Will make the purpose of two transactions clear.

    Also, the names are confusing, something more distinct would be helpful.


    jonatack commented at 9:32 PM on August 7, 2021:

    Interesting. We use and need all three transactions, AFAICT. Updated the names in the last commit to txid, txid2, and txid3. The test still passes if we sync_blocks() for the second and third transactions instead of sync_all(), but I'd rather not mess with this here.


    jonatack commented at 10:31 AM on August 8, 2021:

    Took a fresh look this morning, good call! The first conditional in CTransactionRef GetTransaction() was not necessarily being exercised by the tests:

        if (mempool && !block_index) {
            CTransactionRef ptx = mempool->get(hash);
            if (ptx) return ptx;
        }
    

    Added this assert in 7f7e64e30376a2 that covers it:

                 # 1. valid parameters - supply txid along with various valid values for verbose
    +            if n == 0 or n == 3:
    +                # test with a tx in mempool, with and without -txindex
    +                tx_in_mempool = self.nodes[n].sendtoaddress(self.nodes[n + 1].getnewaddress(), 0.1)
    +                self.nodes[n].getrawtransaction(tx_in_mempool)
    

    Verified by removing that code in GetTransaction() to ensure the new assert fails without it and passes with it.

  21. rajarshimaitra commented at 3:09 PM on July 23, 2021: contributor

    tACK https://github.com/bitcoin/bitcoin/pull/22437/commits/d27edf1d85f44c58029e0197251cca56873b860f.

    verified that the test passes with #22383 changes.

    Below are few comments regarding test coverage and arrangements.

  22. practicalswift commented at 8:04 PM on July 24, 2021: contributor

    Concept ACK

  23. in test/functional/rpc_rawtransaction.py:107 in d27edf1d85 outdated
     125 | +        self.generate_and_sync(node=0, blocks=1)
     126 | +
     127 | +        # Make a tx by sending, then generate 2 blocks; block1 has the tx in it
     128 | +        tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
     129 | +        block1, block2 = self.nodes[2].generate(2)
     130 | +        self.sync_peers()
    


    rajarshimaitra commented at 2:38 PM on August 8, 2021:

    It seems to me that sync_peers() is not doing anything and all the nodes get the blocks with generate(). This is probably because generate() already broadcasts the blocks in p2p.


    jonatack commented at 6:07 PM on August 8, 2021:

    Done


    jonatack commented at 10:04 AM on August 9, 2021:

    Moving the in-mempool transaction tests commit after the simplification commit revealed that removing the self.sync_peers() would be flakey without the presence of the in-mempool transaction. This could be fragile, so no longer removing it in the simplification commit.

  24. jonatack force-pushed on Aug 8, 2021
  25. jonatack commented at 3:29 PM on August 8, 2021: member

    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 (review)):

    git range-diff db94d74 d27edf1 7f7e64e

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

  26. jonatack force-pushed on Aug 8, 2021
  27. rajarshimaitra commented at 4:40 PM on August 8, 2021: contributor

    Thanks @jonatack for considering the comments.

    Re tested ACK https://github.com/bitcoin/bitcoin/pull/22437/commits/afb4eab7a3d309c34357aa6ac54b03bebd15adfb

    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.

  28. jonatack commented at 5:13 PM on August 8, 2021: member

    @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.

    <details><summary>code diff</summary><p>

    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)
    

    </p></details>

    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.

  29. jonatack commented at 6:11 PM on August 8, 2021: member

    @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.

  30. jonatack force-pushed on Aug 8, 2021
  31. fanquake commented at 7:14 AM on August 9, 2021: member

    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 https://github.com/bitcoin/bitcoin/pull/22437/commits/10a3db049ce2858b4885472314567d05d6cc75cb you rename variables i.e tx, txId to txid2, txid3 etc. However in the following commit (https://github.com/bitcoin/bitcoin/pull/22437/commits/c2d79955a833aec09ad2f7a064509bcdcdbd1cb0), a bunch of those end up being renamed again, i.e txid3 and txid2 back to tx.

  32. jonatack force-pushed on Aug 9, 2021
  33. jonatack commented at 10:00 AM on August 9, 2021: member

    @fanquake yes, that's better. Reordered the last three commits and reduced the diff slightly in others.

  34. jonatack force-pushed on Aug 9, 2021
  35. rajarshimaitra commented at 5:32 PM on August 9, 2021: contributor

    tACK https://github.com/bitcoin/bitcoin/pull/22437/commits/f0aacf0a87a67f88018f683092f3b7d16e03e6f1

    @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.

    That seems correct. That's very generous of you. You did the work, i just pointed fingers.

    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.

    Yes that occurred to me too. There is no observational difference between the two paths. So we won't know which one is taken. Probably something for a future improvement. Maybe all it needs is one debug log in the core logic, and with that we can check in the test.

  36. josibake commented at 9:47 AM on August 20, 2021: member

    ACK https://github.com/bitcoin/bitcoin/pull/22437/commits/f0aacf0a87a67f88018f683092f3b7d16e03e6f1

    code review (tons of great suggestions from others, so nothing for me to add), compiled and ran the tests locally, switched the -txindex nodes to non-txindex and vice-versa to verify tests failed as expected.

    overall, major kudos on the refactor. grouping the tests into functions made this much more readable. also +1 for more test coverage

  37. josibake approved
  38. in test/functional/rpc_rawtransaction.py:320 in f0aacf0a87 outdated
     574 | -        assert_equal(decrawtx['version'], 0x7fffffff)
     575 | -
     576 | -        self.log.info('sendrawtransaction/testmempoolaccept with maxfeerate')
     577 | +    def sendrawtransaction_testmempoolaccept_tests(self):
     578 | +        self.log.info("Test sendrawtransaction/testmempoolaccept with maxfeerate")
     579 | +        fee_exceeds_max = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"
    


    Zero-1729 commented at 10:52 AM on August 21, 2021:

    What's the reason for having this as a variable instead of passing the literal to assert_raises_rpc_error as an arg?


    jonatack commented at 3:47 PM on August 21, 2021:

    This variable is invoked twice below in this test, so it clarifies that we check for the same message and also allows reducing the line length to under 120; previously for instance, both lines did not fit in the GitHub display without scrolling (and scrolling is an annoyance with a file or diff of this length, as you have to navigate to the bottom, scroll, then navigate back to the lines).


    Zero-1729 commented at 4:44 PM on August 21, 2021:

    Thanks for clarifying!

  39. Zero-1729 approved
  40. Zero-1729 commented at 10:52 AM on August 21, 2021: contributor

    crACK f0aacf0

    Nice changes! Local tests also passed 🧉

  41. jonatack commented at 8:42 AM on August 24, 2021: member

    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 :)

  42. in test/functional/rpc_rawtransaction.py:233 in 01fba512be outdated
     226 | @@ -226,28 +227,28 @@ def run_test(self):
     227 |          tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
     228 |          block1, block2 = self.nodes[2].generate(2)
     229 |          self.sync_all()
     230 | -        # We should be able to get the raw transaction by providing the correct block
     231 | -        gottx = self.nodes[0].getrawtransaction(tx, True, block1)
     232 | -        assert_equal(gottx['txid'], tx)
     233 | -        assert_equal(gottx['in_active_chain'], True)
     234 | -        # We should not have the 'in_active_chain' flag when we don't provide a block
    


    MarcoFalke commented at 2:21 PM on August 26, 2021:

    in c7327a6e90: Why is this test removed and re-added in the next commit? I think it would be easier to review if it was kept.


    jonatack commented at 3:08 PM on August 26, 2021:

    Probably a rebase oversight during the various changes.


    MarcoFalke commented at 5:38 PM on August 31, 2021:

    Not addressed in the latest force push? The first two commits aren't squashed?


    jonatack commented at 6:06 PM on August 31, 2021:

    Thanks for having a look. Didn't change any commits, only dropped the last ones to ~preserve the previous review ACKs.


    MarcoFalke commented at 6:09 PM on August 31, 2021:

    reviewing that two commits are squashed should be as easy as checking that other commits are dropped, no?


    jonatack commented at 7:42 PM on August 31, 2021:

    squashed

  43. in test/functional/rpc_rawtransaction.py:231 in 01fba512be outdated
     248 | -        gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1)
     249 | -        assert_equal(gottx['in_active_chain'], False)
     250 | -        self.nodes[0].reconsiderblock(block1)
     251 | -        assert_equal(self.nodes[0].getbestblockhash(), block2)
     252 | +        for n in [0, 3]:
     253 | +            self.log.info(f"Test getrawtransaction {'with' if n == 0 else 'without'} -txindex, with blockhash")
    


    MarcoFalke commented at 2:22 PM on August 26, 2021:

    c7327a6e90: Don't all nodes have txindex, except n=3? At least the extra_args would suggest that.


    jonatack commented at 3:02 PM on August 26, 2021:

    Yes, kept node 0 as it was already in use for the -txindex case, and added node 3 to test without -txindex. Didn't seem worth adding iterating through nodes 1 and 2.


    jonatack commented at 3:18 PM on August 26, 2021:

    (this does indeed work:)

    -        for n in [0, 3, 4, 5]:
    +        for n in range(self.num_nodes):
                 self.log.info(
    -                f"Test getrawtransaction {'with' if n == 0 or n == 5 else 'without'} -txindex,"
    +                f"Test getrawtransaction {'with' if n <= 2 or n == 5 else 'without'} -txindex,"
                     f" {'with' if n >= 4 else 'without'} -blocksonly"
                 )
                 # 1. valid parameters - supply txid along with various valid values for verbose
    -            if n == 0 or n == 3:
    +            if n <= 3:
                     # with a transaction in the mempool, with and without -txindex
                     tx_in_mempool = self.nodes[n].sendtoaddress(self.nodes[n + 1].getnewaddress(), 0.5)
                     self.nodes[n].getrawtransaction(tx_in_mempool)
    -            if n == 0 or n == 5:
    +            if n <= 2 or n == 5:
                     # with -txindex
    

    MarcoFalke commented at 3:20 PM on August 26, 2021:

    No need to change. I missed that this was only picking two nodes.

  44. in test/functional/rpc_rawtransaction.py:83 in 75d1e4cb14 outdated
      78 | +        self.sync_blocks()
      79 | +
      80 | +    def generate_and_sync(self, *, node, blocks, pre_sync=True):
      81 | +        if pre_sync == True:
      82 | +            self.sync_peers()
      83 | +        self.nodes[node].generate(blocks)
    


    MarcoFalke commented at 3:26 PM on August 26, 2021:

    75d1e4cb14a4bc293a17e516598a1b6b81dcac4a: If you rebase, you can use self.generate(nodes[node], blocks)


    jonatack commented at 2:13 PM on August 31, 2021:

    75d1e4c: If you rebase, you can use self.generate(nodes[node], blocks)

    Thanks. Dropped the commit.

  45. in test/functional/rpc_rawtransaction.py:78 in 0e4cb3a4bb outdated
      74 | @@ -74,12 +75,11 @@ def setup_network(self):
      75 |          self.connect_nodes(0, 2)
      76 |  
      77 |      def sync_peers(self):
      78 | -        self.sync_mempools()
      79 | +        self.sync_mempools(self.nodes[0:3])  # nodes 0 to 3 have a mempool
    


    MarcoFalke commented at 3:28 PM on August 26, 2021:

    0e4cb3a4bbbf464376b5cad78e83328444bdcf71: The forth node has a mempool, too?


    MarcoFalke commented at 3:34 PM on August 26, 2021:

    How does -blocksonly even affect the test, given that all txs are mined into blocks anyway?


    jonatack commented at 3:48 PM on August 26, 2021:

    If this line remains self.sync_mempools(), the test fails with AssertionError: Mempool sync timed out.

    The last commit adds an in-mempool tx.


    MarcoFalke commented at 3:54 PM on August 26, 2021:

    The last commit adds an in-mempool tx.

    The first tx will be mined into a block immediately after. The second test isn't run on the -blocksonly nodes, unless I am missing something.

    In general -blocksonly only skips incoming transactions, so anything you can test with the option is also possible by simply querying a txid that doesn't exist at all.


    jonatack commented at 4:53 PM on August 26, 2021:

    Do you think it would be better coverage to run the in-mempool test on the -blocksonly nodes as well...

    @@ -88,7 +88,8 @@ class RawTransactionsTest(BitcoinTestFramework):
             for amount in [1.5, 1.0, 5.0]:
                 self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), amount)
    -        self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), 1)
    +        for n in [3, 4, 5]:
    +            self.nodes[0].sendtoaddress(self.nodes[n].getnewaddress(), 1)
             self.generate_and_sync(node=0, blocks=5)
    @@ -109,16 +110,22 @@ class RawTransactionsTest(BitcoinTestFramework):
                 "No such mempool transaction. Use -txindex or provide a block hash to enable"
                 " blockchain transaction queries. Use gettransaction for wallet transactions."
             )
    +        err_msg_2 = "No such mempool or blockchain transaction. Use gettransaction for wallet transactions."
             for n in [0, 3, 4, 5]:
                 self.log.info(
                     f"Test getrawtransaction {'with' if n == 0 or n == 5 else 'without'} -txindex,"
                     f" {'with' if n >= 4 else 'without'} -blocksonly"
                 )
    -            # 1. valid parameters - supply txid along with various valid values for verbose
    +            # With a transaction in the mempool
    +            tx_in_mempool = self.nodes[n].sendtoaddress(self.nodes[n].getnewaddress(), 0.5)
                 if n == 0 or n == 3:
    -                # with a transaction in the mempool, with and without -txindex
    -                tx_in_mempool = self.nodes[n].sendtoaddress(self.nodes[n + 1].getnewaddress(), 0.5)
    +                # with and without -txindex
                     self.nodes[n].getrawtransaction(tx_in_mempool)
    +            else:
    +                # -blocksonly, with and without -txindex
    +                assert_raises_rpc_error(-5, err_msg if n == 4 else err_msg_2, self.nodes[n].getrawtransaction, tx_in_mempool)
    

    or do you prefer dropping the -blocksonly nodes and testing the other nodes with a non-existing tx.


    MarcoFalke commented at 5:50 PM on August 26, 2021:

    Unless I am missing something, the goal of this test is to cover GetTransaction, which itself is unaffected by the blocksonly option. Thus, there is no need to test this option. (Otherwise, there would also be reason to test with mocktime, minrelaytxfee, ...)


    jonatack commented at 10:10 AM on August 31, 2021:

    The -blocksonly nodes allow us to add missing test coverage for rpc getrawtransaction error cases like in my #22437 (review) above. We could maybe pass non-existing txids instead, but it seems better to test actual -blocksonly behavior.

    I have a branch to add that coverage and some other improvements, and rather than increase the size of this pull and invalidate the existing review, I can propose it as a follow-up.


    MarcoFalke commented at 10:15 AM on August 31, 2021:

    it seems better to test actual -blocksonly behavior.

    blocksonly is a network option, so it seems confusing to test it's behaviour inside a raw transaction test.


    jonatack commented at 10:43 AM on August 31, 2021:

    My thought was that -blocksonly effectively disables mempool functionality, which for end-to-end testing relates to how getrawtransaction behaves when a node operator running -blocksonly calls getrawtransaction, which is valid with either txindex and/or passing blockhash if the tx is in a block, so it seems good to have explicit coverage that serves as a regression test and sanity check. Passing a wrong txid could be added as well, where relevant, to verify the same outcome.


    MarcoFalke commented at 11:29 AM on August 31, 2021:

    -blocksonly effectively disables mempool functionality

    The behaviour of the mempool module isn't affected by blocksonly at all. (All txs that are submitted to the mempool are added/rejected in the same way regardless of blocksonly.) blocksonly is purely a net-processing option to disable incoming tx relay from network peers. It doesn't affect (in)validity of txs submitted to the mempool. For example, when the wallet or rpc submits a tx, it will still be added to the mempool.

    As part of this test, if the goal is to get a fully functioning, but empty mempool, it might be easier to run the tests that you want to run at the beginning with an empty mempool. (Or by restarting a node with -nopersistmempool to empty the mempool). Or as mentioned previously by picking a txid that is not currently in the mempool.

    If there was an option to completely disable the mempool (i.e. never initialize the class), then it indeed would make a lot of sense to check for that code path.

    In testing it is important to be able to draw units/modules and assume that the modules don't interact with each other, other than over their defined interface. This allows to test units/modules separate from each other. If they had to be tested in combination it would lead to path explosion in any real software project, thus making it harder to test meaningful.

    Moreover, if the behaviour of one module is required for testing another module, it will make it harder to change the behaviour of that module and only touching the tests of that module.

    Or put another way: If -blockonly was relevant to test here, why is -minrelaytxfee (or any other setting) not relevant to test here?


    jonatack commented at 11:45 AM on August 31, 2021:

    I agree from a unit test point of view, but for end-to-end testing these interactions can be good to test. For example, I think I've found a parameter interaction that doesn't work as documented (not sure yet, need to look further). That seems like good coverage to have if someone proposes it, it is reviewed, and ready to use. The asmap/addrman interaction is another example. What to do here? Lots of time invested so far. Sure, I could spend even more time to rework this without the coverage and hope for everyone to review it again. Or just drop the work starting with "Test...with -blocksonly" and maybe pick up the rest later in a follow-up.


    MarcoFalke commented at 11:56 AM on August 31, 2021:

    The asmap/addrman interaction is another example

    I think asmap is part of the addrman module, so testing those together in a test makes sense.


    jaysonmald35 commented at 6:16 PM on August 31, 2021:

    Thank u

  46. jonatack force-pushed on Aug 31, 2021
  47. jonatack commented at 2:32 PM on August 31, 2021: member

    Dropped the commits after d426cab86c1b, 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.

  48. Test src/node/transaction::GetTransaction() without -txindex 7f073594c9
  49. refactor: dedup/reorg createrawtransaction sequence number tests 8c19d1329f
  50. refactor: txid to constant in rpc_rawtransaction to isolate tests 0097740773
  51. test: run 2nd getrawtransaction section with/without -txindex
    (and make the 'string "Flase"' test clearer as requested by reviewers)
    85d8869cf8
  52. jonatack force-pushed on Aug 31, 2021
  53. jonatack commented at 7:47 PM on August 31, 2021: member

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

  54. test: add and harmonize getrawtransaction logging 14398b30d6
  55. test: remove no longer needed (ASCII art) comments d861040dd2
  56. move-only: regroup similar rpc_rawtransaction tests together 409779df95
  57. refactor: separate the rpc_rawtransaction tests into functions 7d5cec2e49
  58. test, refactor: rpc_rawtransaction PEP8 387355bb94
  59. jonatack force-pushed on Aug 31, 2021
  60. mjdietzx commented at 8:52 PM on August 31, 2021: contributor

    reACK 387355bb9482a09c1fc9b137bea56745a93b7dfd

  61. fanquake deleted a comment on Sep 1, 2021
  62. MarcoFalke commented at 4:16 PM on September 1, 2021: member

    nice. Thanks.

    Approach ACK 387355bb9482a09c1fc9b137bea56745a93b7dfd 🔆

    <details><summary>Show signature and timestamp</summary>

    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 -

    </details>

  63. MarcoFalke merged this on Sep 1, 2021
  64. MarcoFalke closed this on Sep 1, 2021

  65. jonatack deleted the branch on Sep 1, 2021
  66. in test/functional/rpc_rawtransaction.py:105 in 387355bb94
     105 | +        self.nodes[0].generate(1)
     106 | +        self.sync_all()
     107 | +        vout = find_vout_for_address(self.nodes[1], txid, addr)
     108 | +        rawTx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): 9.999})
     109 | +        rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx)
     110 | +        txId = self.nodes[1].sendrawtransaction(rawTxSigned['hex'])
    


    MarcoFalke commented at 2:40 PM on January 6, 2022:
                                       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...


    MarcoFalke commented at 2:52 PM on January 6, 2022:

    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")
    

    jonatack commented at 2:58 PM on January 6, 2022:

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


    jonatack commented at 3:14 PM on January 20, 2022:
  67. DrahtBot locked this on Jan 20, 2023

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-13 15:14 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me