-
Notifications
You must be signed in to change notification settings - Fork 38.7k
test: Refactor rpc_getblockstats.py to use MiniWallet #33184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code Coverage & BenchmarksFor details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33184. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please copy-paste ConflictsReviewers, this pull request conflicts with the following ones:
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. |
545aafb to
10da636
Compare
kannapoix
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm new to this code base, so I might have missed something or made some mistakes in my review.
Please feel free to correct me if that's the case. Thanks!
| external_address = self.nodes[0].get_deterministic_priv_key().address | ||
| external_script = address_to_scriptpubkey(external_address) | ||
|
|
||
| for i, (amount_btc, _) in enumerate(TRANSACTIONS): | ||
| amount_satoshis = int(amount_btc * COIN) | ||
| wallet.send_to( | ||
| from_node=self.nodes[0], | ||
| scriptPubKey=external_script, | ||
| amount=amount_satoshis, | ||
| ) | ||
| if i == 0: | ||
| self.generate(wallet, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are not specifically testing address type or scripts here, perhaps we could use wallet.send_self_transfer(from_node=self.nodes) instead?
I think this might make the code simpler than generating scripts and using wallet.send_to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, but send_self_transfer() only creates self-transfers (sending back to the wallet's own address). For this test, we need external outputs to properly test getblockstats UTXO statistics. The send_to() method is necessary here to create transactions with external recipients.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't quite understand why specifically external outputs are necessary.
In getblockstats, there are four UTXO-related statistics: utxo_increase, utxo_size_inc, utxo_increase_actual, and utxo_size_inc_actual.
As far as I can see, none of these depend on whether a UTXO is sent to an external or internal address.
That said, using send_self_transfer would significantly change the generated transactions compared to the original test, so I agree that it's better to try to keep the generated transactions as close to the original as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're correct that UTXO statistics don't depend on output destination. However, I'm keeping external outputs to maintain consistency with the original test's transaction structure. Changing to send_self_transfer would alter the generated test data and expected statistics values.
Since the goal is to replace the wallet implementation while preserving test behavior, send_to with external scripts is the most faithful translation of the original sendtoaddress calls.
| scriptPubKey=external_script, | ||
| amount=amount_satoshis, | ||
| ) | ||
| if i == 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I ask why we need to mine here after the first transaction?
I noticed that the original test also mines at this point, but I’m not sure about the reason behind it:
bitcoin/test/functional/rpc_getblockstats.py
Lines 53 to 54 in 6ca6f3b
| self.nodes[0].sendtoaddress(address=address, amount=10, subtractfeefromamount=True) | |
| self.generate(self.nodes[0], 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mining pattern is intentional - it creates a specific block structure for testing:
- Block 1: 1 transaction (first 10 BTC send)
- Block 2: 4 transactions (remaining sends + OP_RETURN)
This allows testing getblockstats across different block scenarios (single vs multi-transaction blocks) rather than having all transactions in one block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
It appears that we are no longer testing the case of send 10 BTC with fee subtracted, nor the case of send 1 BTC with a higher fee rate, since we are not specifying a fee in send_to().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're correct - the comments were misleading. I've updated them to accurately reflect that MiniWallet uses consistent fee behavior for all transactions. The test still validates getblockstats functionality across different block structures, which is the primary goal.
16ca1aa to
3321cad
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
3321cad to
5544dae
Compare
|
Concept ACK |
0d06794 to
2e1836d
Compare
That's not why it fails, it does not use anything specific to legacy wallets at all. It fails initially because test nodes are started with Ultimately, the issues stem from the fact that this code path is pretty much never executed, and if MiniWallet were to change in the future, we'd run into the same problem. |
Thanks for the review @achow101 Originally assumed MiniWallet was the culprit based on the issue comments, but your explanation makes sense: the fixes here does not seem to be a proper solution to the problem. However, I believe the MiniWallet refactor is still the right approach here. The fundamental issue is that the --gen-test-data code path uses outdated testing patterns that are prone to breaking with framework changes. By modernizing to MiniWallet, we ensure the test follows current best practices and stays compatible with future framework updates. The alternative would be to just add self.add_wallet_options(parser) and replace deprecated RPCs, but that leaves the test using legacy patterns that could break again. What do you think would be the most effective long-term solution? |
|
Looked into this @achow101 and you're right about the setup issues - they weren't legacy wallet specific but rather configuration problems.
Regarding your concern about the --gen-test-data path being rarely executed: I've ensured the test runs with proper wallet configuration, making it more likely to be executed in CI and catching issues earlier. While this PR may seem like overkill for the specific bug, MiniWallet does provide better maintainability and avoids the deprecated RPC patterns you mentioned. The changes are minimal and focused on the core issues. |
You'll have to adjust the pull description. Also, CI fails. To ensure this is tested once, you can add |
09df27a to
316cf41
Compare
Eunovo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test could be simplified to always generate data with the MiniWallet and test with generated data, instead of dumping it to a file and loading it later. I don't see the advantage of "generate" and "load" paths, and it's creating unnecessary complexity.
I guess it depends on how long it takes to generate, but if it is just a few seconds, it seems easier not to cache. |
a367a89 to
ac7f694
Compare
863da27 to
aeb18a1
Compare
|
I was reading the code without having seen the discussions here, only based on PR title and description. With some context from the discussions, I think both of them should be updated to reflect what this PR is really doing. It was asked before (see #33184 (comment)) but maybe it still needs improvement. |
- Replace legacy wallet usage with MiniWallet for better test isolation. - Keep mocktime to ensure deterministic block timestamps in stats, preventing CI failures due to varying 'time' fields in getblockstats. - Add logging for generated block data to aid debugging. - Fix test logic to avoid no-op conditions
aeb18a1 to
b89afc6
Compare
I have updated the PR title and description based on the conversations made to better depict what the PR is doing @brunoerg . |
|
In commit b89afc6
|
| if i == 0: | ||
| self.generate(wallet, 1) | ||
|
|
||
| self.sync_all() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need sync_all here? self.generate() already performs synchronization, and this test uses a single node. What are we syncing at this point?
| # Make sure all stats have the same set of keys | ||
| expected_keys = set(stats[0].keys()) | ||
| for stat in stats[1:]: | ||
| assert_equal(set(stat.keys()), expected_keys) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing that all stats share the same set of keys is different from testing that the required keys are present. To preserve the original test’s intent, should we hard-code the expected keys? Other tests follow this approach as well :
bitcoin/test/functional/rpc_blockchain.py
Line 135 in 85d058d
| def _test_getblockchaininfo(self): |
|
|
||
| for i in range(self.max_stat_pos+1): | ||
| self.log.info('Checking block %d' % (i)) | ||
| assert_equal(stats[i], self.expected_stats[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn’t removing these assertions degrade the test? The original test compared RPC results against predefined expected stats.
| blockhash = stats[i]['blockhash'] | ||
| stats_by_hash = self.nodes[0].getblockstats(hash_or_height=blockhash) | ||
| assert_equal(stats_by_hash, self.expected_stats[i]) | ||
| assert_equal(stats_by_hash, stats[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this also have a similar problem I pointed out before: https://github.com/bitcoin/bitcoin/pull/33184/files#r2567593821
This changes the test from testing against predefined expected data to cross-checking one getblockstats call with another (by hash). That’s a different test which may degrade the test.
|
|
||
| if __name__ == '__main__': | ||
| GetblockstatsTest(__file__).main() | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe these are added by mistake? Same lines are already exist above.
|
🐙 This pull request conflicts with the target branch and needs rebase. |
Refactors rpc_getblockstats.py to use MiniWallet for transaction creation, removing legacy wallet dependencies and simplifying the test by eliminating data caching.
Fixes #31838