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.
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-
jonatack commented at 1:23 PM on July 13, 2021: member
- fanquake added the label Tests on Jul 13, 2021
-
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 - jonatack force-pushed on Jul 13, 2021
-
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.
- jonatack force-pushed on Jul 14, 2021
- jonatack force-pushed on Jul 14, 2021
-
mjdietzx commented at 7:46 PM on July 14, 2021: contributor
ACK 993189b1fe39cfc29e960ea3a20092309001fa8f very nicely done!
- jonatack force-pushed on Jul 15, 2021
-
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)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
gottxis in hex?
jonatack commented at 10:57 AM on August 8, 2021:This assert tests that the
in_active_chainfield is absent as expected when the blockhash argument is not passed...seesrc/rpc/rawtransaction.cpp::getrawtransaction()orbitcoin-cli help getrawtransaction
kiminuo commented at 12:38 PM on August 8, 2021:Just to make myself more clear: L140 ends with
['hex']so presumablygottxis a string, so testing on L142 whethergottxdoes not containin_active_chainseems 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!
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_peersshould 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 mempoolin 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
txidandtxIdare 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
txIdassignment; fixed.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
txIdinstead oftxidhere. Or can you explain this please?
jonatack commented at 4:40 PM on August 7, 2021:Good catch! This illustrates why using
txidandtxIdvariable names in the same test isn't ideal. Fixed in commit "Test src/validation::GetTransaction() with -blocksonly".lsilva01 approvedlsilva01 commented at 11:10 PM on July 22, 2021: contributorTested ACK https://github.com/bitcoin/bitcoin/pull/22437/commits/d27edf1d85f44c58029e0197251cca56873b860f on Ubuntu 20.04
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.
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 test5and6should be moved intocreaterawtransaction()test group?
jonatack commented at 7:24 PM on August 7, 2021:Good idea! Done in a3d8f790633b89a.
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
txidandtxIdtransactions helps here, given both of them are confirmed. Because of that we are also not getting a test for onlymempooltransactions.So maybe we can have a
txidconfirmed in a block, and then havetxIdin 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.
rajarshimaitra commented at 3:09 PM on July 23, 2021: contributortACK 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.
practicalswift commented at 8:04 PM on July 24, 2021: contributorConcept ACK
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 withgenerate(). This is probably becausegenerate()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.jonatack force-pushed on Aug 8, 2021jonatack commented at 3:29 PM on August 8, 2021: memberRebased 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 7f7e64eThank you @mjdietzx, @lsilva01, and @rajarshimaitra for the ACKs. Would you mind re-ACKing?
jonatack force-pushed on Aug 8, 2021rajarshimaitra commented at 4:40 PM on August 8, 2021: contributorThanks @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_testssetup 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
txindexis on, theblockhashsearching 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 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.
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.jonatack force-pushed on Aug 8, 2021fanquake commented at 7:14 AM on August 9, 2021: memberIt 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,txIdtotxid2,txid3etc. However in the following commit (https://github.com/bitcoin/bitcoin/pull/22437/commits/c2d79955a833aec09ad2f7a064509bcdcdbd1cb0), a bunch of those end up being renamed again, i.etxid3andtxid2back totx.jonatack force-pushed on Aug 9, 2021jonatack force-pushed on Aug 9, 2021rajarshimaitra commented at 5:32 PM on August 9, 2021: contributortACK 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.
josibake commented at 9:47 AM on August 20, 2021: memberACK 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
-txindexnodes 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
josibake approvedin 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_erroras 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!
Zero-1729 approvedZero-1729 commented at 10:52 AM on August 21, 2021: contributorcrACK f0aacf0
Nice changes! Local tests also passed 🧉
jonatack commented at 8:42 AM on August 24, 2021: memberThanks 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 :)
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
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.
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)
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 withAssertionError: 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
-blocksonlyonly 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 theblocksonlyoption. Thus, there is no need to test this option. (Otherwise, there would also be reason to test withmocktime,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
-nopersistmempoolto 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
-blockonlywas 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
jonatack force-pushed on Aug 31, 2021jonatack commented at 2:32 PM on August 31, 2021: memberDropped 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.
Test src/node/transaction::GetTransaction() without -txindex 7f073594c9refactor: dedup/reorg createrawtransaction sequence number tests 8c19d1329frefactor: txid to constant in rpc_rawtransaction to isolate tests 009774077385d8869cf8test: run 2nd getrawtransaction section with/without -txindex
(and make the 'string "Flase"' test clearer as requested by reviewers)
jonatack force-pushed on Aug 31, 2021jonatack commented at 7:47 PM on August 31, 2021: memberSquashed the first two commits per review feedback and made some minor improvements per
git diff d426cab 387355b.test: add and harmonize getrawtransaction logging 14398b30d6test: remove no longer needed (ASCII art) comments d861040dd2move-only: regroup similar rpc_rawtransaction tests together 409779df95refactor: separate the rpc_rawtransaction tests into functions 7d5cec2e49test, refactor: rpc_rawtransaction PEP8 387355bb94jonatack force-pushed on Aug 31, 2021mjdietzx commented at 8:52 PM on August 31, 2021: contributorreACK 387355bb9482a09c1fc9b137bea56745a93b7dfd
fanquake deleted a comment on Sep 1, 2021josibake commented at 6:31 AM on September 1, 2021: memberMarcoFalke commented at 4:16 PM on September 1, 2021: membernice. 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>
MarcoFalke merged this on Sep 1, 2021MarcoFalke closed this on Sep 1, 2021jonatack deleted the branch on Sep 1, 2021in 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.
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