-
Notifications
You must be signed in to change notification settings - Fork 38.7k
wallet, bugfix: fix ComputeTimeSmart function during rescanning process. #20591
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
wallet, bugfix: fix ComputeTimeSmart function during rescanning process. #20591
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. 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. |
|
Is it right this way ? |
|
Yes, thank you. |
|
Concept ACK. |
jonatack
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.
Concept ACK
|
Thanks for all the feedback. I'll correct everything and ask question if needed. |
|
Concept ACK. Thanks! |
|
Concept ACK - code looks good (haven't looked at the tests). Code is relatively simple, basically just an |
0fdd53f to
05f71bf
Compare
|
I've push a new version with the rework marked as resolved done.
|
05f71bf to
e5e2a30
Compare
|
I've push the full rework and add documentation update proposal in a separated commit. |
e5e2a30 to
ace3f77
Compare
|
Weak NACK. Seems like a hacky "fix" for what is pretty much a user error...? |
|
Thanks for your feedback. I'll try to give a broader view of the problem and of my fix, from my perspective. First of all, the current behaviour seems to be present for years, and no recent modification has changed this. The issue that triggered my investigation is this one : #20181 The thing is that when someone encounters this behavior from Bitcoin Core wallet, it clearly has no clue on what's going on, because it's hard to reproduce it and the date seems so random and nonsense, that there's no easy way to request for similar problems on search engines. And when you try to investigate it, nothing points you in the right direction. If it's considered as an error from the RPC API user to proceed first with a recent scan, and after with an older one, it must be documented in the RPC API Reference. Here why I think it might be a good idea to fix it : the API RPC users are mainly teams working around Bitcoin Core to enrich the ecosystem with more tools.
I found all these ideas interesting for a faster and better user experience and guess what, they had a bug that broke my wallet, and pushed me to look at the problem. Now let's go back to the fix itself. Finally this PR approach was a way of keeping the patch simple while preserving functionality.
Happy to pursue the discussion here. |
How is rescanning blockchain a user error? |
|
Manually scanning out of order, to be specific, is a user error because the wallet is assumed to always be scanned up to its sync-point. |
|
@luke-jr I'd agree the wallet is written with that assumption, but I also don't see a downside to changing the behavior when the wallet can know it's dealing with an unusual situation. I haven't dug into the details, but I suspect this change makes things match expectations in strictly more situations. |
|
Concept ACK |
ryanofsky
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.
Code review ACK ace3f77597a04785c2ab98708f31970934197044. This is great! A trivial fix replacing currently insane behavior (using latest transaction times to estimate times of transactions in old blocks) with the simple and obvious behavior of using the block timestamp as the transaction timestamp.
The only suggestion I would make is to maybe use block max time instead of block time for these transactions, to ensure that times within the set of rescanned transactions are monotonically increasing. (You can get the max time by chaining the FoundBlock expression FoundBlock().time(blocktime).maxTime(block_max_time)).
I will say I don't actually understand Luke's objection to this, and would be curious for an explanation. I understand (despite disagreeing with) the idea that it may be good to alert users that something is wrong by showing bad timestamps when they do something that the wallet can't support. But it's not clear what the exact user mistake is here, and if there is a better way to alert users about the bad usage, or just add code supporting the usage.
|
Another thing I think could be done to improve this is to avoid adding these transactions to Lines 873 to 874 in ea96e17
If we want to exclude these transactions from smart time calculation, better to fully exclude instead of half-excluding them. Maybe there is a way to adjust the smart time calculations to include them, but that would require more thought and might not be worth the effort. Another code cleanup that would be nice to see is moving IMO, previous ACK is still ok and switching from block time to max block time, excluding these transactions from wtxOrdered, and making |
You must encounter a similar problem as the one that make me code this PR. You can't recovered a wallet with incorrect datation. It's not possible yet, even with this PR : the only way is to destroy and recreate it. This PR only prevent the datation to be corrupted in the first place. |
ryanofsky
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.
Code review ACK bb6bc61bea3098d4dab895a73c2c0f7b8d293e2b. No significant changes since last review other than rebase, optimizing test, and switching to max block time instead of block time.
Thanks for looking into txordered suggestions! It'd be nice to clean up this code more but I can see how listtransaction constrains changing txordered, and I can see upsides of keeping ComputeTimeSmart functional even I might still prefer to see time logic less spread out over two method. There's space for more improvements later but I think this PR is clearly better than status quo.
One thing I don't know is what problem may cause the other reported issue #20591 (comment). Might be worth filing separate bug if there are known steps to reproduce.
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.
How about nTimeSmart = min(nTimeSmart, block_max_time) and avoid rescanning_old_block argument?
Could you elaborate where do you imagine using it ? Because min function between nTimeSmart and blocktime is already present in the current Bitcoin Core code and doesn't prevent the original problem. I'm refering to this line : If you imagine introduce another minimum function after the previous one, you'll invalidate the maximum function with lastestEntry and change the original behavior of the ComputeTimeSmart function. This PR solve the issue without removing existing functionality. |
Thanks ryanofsky for your feedback and your code review ! For the [#20591 (comment)], I need some confirmation from @charlierlee on my analysis : My point is that in that case : this PR won't save you because you can't recover an incorrect datation. It's an unrecoverable state. I mention in a previous comment "there's no way to correct transaction datation later : the only way is to destroy the wallet.". The rescan function never change a previous datation (even with a full rescan). That's why I think this PR is important, because it prevent the bad datation to occured in the first place. |
|
For the [#20591 (comment)], @BitcoinTsunami yes that is correct. |
|
@promag did want to follow up on #20591 (comment)? To me this PR seems like a straightforward improvement that gets the rescan context taken into account in a simple way and adds test coverage. I'm sure there are other ways it could be improved beyond this, but I think this change should only help make future improvements and not get in the way. |
bb6bc61 to
b92d552
Compare
|
Same code. I've just rebased from the latest master code to resolve conflict. |
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.
Code review ACK b92d5522782a8d56f71f9ed4d04dbb068b9d65e6
Verified the test fails on current master but passes with this PR. Nits inline are non-blocking. Will merge if @ryanofsky or someone else re-ACKs.
Thanks @BitcoinTsunami!
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.
Code review re-ACK b92d5522782a8d56f71f9ed4d04dbb068b9d65e6
Edit, also re-verified the debug build is clean and the test passes on the branch and fails on master.
2021-09-28T10:46:57.258000Z TestFramework (INFO): Check transactions time after restoration
2021-09-28T10:46:57.260000Z TestFramework (ERROR): Assertion failed
File "/home/jon/projects/bitcoin/bitcoin/test/functional/wallet_transactiontime_rescan.py", line 154, in run_test
assert_equal(tx['time'], cur_time + ten_days)
AssertionError: not(1635418013 == 1633690013)
Happy to re-review quickly for the suggestions.
ryanofsky
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.
Code review ACK b92d5522782a8d56f71f9ed4d04dbb068b9d65e6. No changes since previous review other than rebase (conflicts on nearby lines)
…ing block rescanning
…IfInvolvingMe() regarding rescanning_old_block parameter
b92d552 to
240ea29
Compare
|
Thanks meshcollider and jonatack for your feedback. |
|
ACK 240ea29 per |
meshcollider
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.
re-utACK 240ea29
…ring rescanning process. 240ea29 doc: update doxygen documention of ComputeTimeSmart() and AddToWalletIfInvolvingMe() regarding rescanning_old_block parameter (BitcoinTsunami) d6eb39a test: add functional test to check transaction time determination during block rescanning (BitcoinTsunami) 07b44f1 wallet: fix ComputeTimeSmart algorithm to use blocktime during old block rescanning (BitcoinTsunami) Pull request description: The function ComputeTimeSmart in wallet.cpp assume that transaction are discovered in the right order. Moreover the 'smarttime' determination algorithm is coded with realtime scenario in mind and not rescanning of old block. The functional test demonstrate that if the user import a wallet, then rescan only recent history, and then rescan the entire history, the older transaction discovered would have an incorrect time determination. In the context of rescanning old block, the only time value that as a meaning is the blocktime. That's why I've fixed the problem with a simple separation between rescanning of old block and realtime time determination. The fix is written to have no impact on every realtime scenario and only impact the behaviour during a rescanning process. This PR Fixes bitcoin#20181. To be fair, I don't think that this bug could be triggered with the wallet GUI, because it always proceed with a proper rescan. But RPC API provide the possibility to trigger it. I've discovered it, because Specter desktop v0.10.0 was impacted. (cryptoadvance/specter-desktop#680). ACKs for top commit: jonatack: ACK 240ea29 per `git diff b92d552 240ea29`, re-verified rebase to latest master + debug build clean + new test passes on the branch and fails on master, only change since my review a few hours ago is incorporation of latest review suggestions meshcollider: re-utACK 240ea29 Tree-SHA512: 514b02e41d011ddfa325f5e8080b93800e1ea4ed5853fa420670a6ac700e8b463000dbea65f8ced8565cfb950c7f51b69154034dcb111e67aca3b964a0061494
The function ComputeTimeSmart in wallet.cpp assume that transaction are discovered in the right order.
Moreover the 'smarttime' determination algorithm is coded with realtime scenario in mind and not rescanning of old block.
The functional test demonstrate that if the user import a wallet, then rescan only recent history, and then rescan the entire history, the older transaction discovered would have an incorrect time determination.
In the context of rescanning old block, the only time value that as a meaning is the blocktime.
That's why I've fixed the problem with a simple separation between rescanning of old block and realtime time determination. The fix is written to have no impact on every realtime scenario and only impact the behaviour during a rescanning process.
This PR Fixes #20181.
To be fair, I don't think that this bug could be triggered with the wallet GUI, because it always proceed with a proper rescan.
But RPC API provide the possibility to trigger it. I've discovered it, because Specter desktop v0.10.0 was impacted. (cryptoadvance/specter-desktop#680).