-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet: bugfix, always use apostrophe for spkm descriptor ID #27920
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, always use apostrophe for spkm descriptor ID #27920
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. 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. |
6b51f24
to
3a38f7f
Compare
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.
Can confirm that this fixes the problem from #27915, I'm not familiar with the wallet though, so I can't comment on the code changes.
3a38f7f
to
494fa79
Compare
Having the id be dependent on Edit: Actually, how the id itself is generated shouldn't matter. It might as well be random. Once we assign the id, we should just store it and remember what it is rather than regenerating it whenever we want it. But that doesn't really solve this downgrading problem. |
494fa79
to
0f294a7
Compare
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.
One way to reduce the odds of this happening again with future descriptor changes is to hardcode the COMPAT
identifier for all the test vectors in descriptor_tests.cpp
.
I wrote a commit on top of this branch that moves https://github.com/Sjors/bitcoin/tree/2023/06/desc_id_vectors |
You know, I initially wrote this with the Also, left you a comment on your commit. Not sure why added the Will go deeper over your commit. Thanks!
Good call 👌🏼. |
9e1c3b4
to
467b7fd
Compare
Updated per feedback, thanks @Sjors. Pulled your test commit and made some changes. While reviewing it, discovered that descriptors containing a key with an origin are actually incorrect. As the origin consists of a root xpub fingerprint and the key derivation path, this former needs to be formatted with an apostrophe as well. So, have updated the code and tests to address it. And also expanded the coverage for few other descriptors that were previously not included. Then, to facilitate the verification of the test vectors, have created two isolated commits, ab3b700 and 9e1c3b4. These can be applied on top of the v25 branch. The cherry-pick process is not entirely clean, but once it's done, you can easily verify the correctness of the descriptors' IDs. To simplify the process of verifying these test vectors, have pushed a v25 branch that includes only those two commits: https://github.com/furszy/bitcoin-core/tree/25.x_verify_spkm_ids. Also, have separated the descriptor ID function as a standalone function to avoid mixing it with the descriptor's interface. |
Glad the tests were useful :-)
Thanks, I was thinking of testing that as well, but couldn't figure out how to do so trivially with my own commit. |
If the computed descriptor's ID doesn't match the wallet's DB spkm ID, return early from the loading process to prevent DB data from being modified in any post-loading procedure (e.g 'TopUp' updates the descriptor's data).
This allows us to verify the descriptor ID on the descriptors unit tests in different software versions without requiring to use the entire DescriptorScriptPubKeyMan machinery. Note: The unit test changes are introduced after the bugfix commit but this commit + the unit test commit can be cherry-picked on top of the v25 branch to verify IDs correctness. IDs must be the same for v25 and after the bugfix commit.
As we update the descriptor's db record every time that the wallet is loaded (at `TopUp` time), if the spkm ID differs from the one in db, the wallet will enter in an unrecoverable corruption state, and no soft version will be able to open it anymore. Because we cannot change the past, to stay compatible between releases, we need to always use the apostrophe version for the spkm IDs.
Tests vectors were calculated by running the same tests on v25. Which was the last release prior to introducing the diff in the descriptor's string representation ('h' format). Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
467b7fd
to
5df988b
Compare
What happened to fa23292? |
Vanished after #24914 merge :). We no longer iterate over all the database records without ordering. Now the wallet loads each specific group of records separately (e.g. legacy records, descriptors records, txs, etc). The reason behind fa23292 was that the upper level method |
ACK: I've tested the changes and the bug is resolved. I've been struggling with back-porting the test-code to v25.0. @Sjors : Thanks for helping me out with this |
ACK 5df988b |
tACK 5df988b Also tested backporting the tests. Trick is to disable the norm_pub check (or manually replacing |
// Prior to doing anything with this spkm, verify ID compatibility | ||
if (id != pwallet->GetDescriptorScriptPubKeyMan(desc)->GetID()) { | ||
strErr = "The descriptor ID calculated by the wallet differs from the one in DB"; | ||
return DBErrors::CORRUPT; |
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.
Why not UNKNOWN_DESCRIPTOR
instead?
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.
The descriptor isn't unknown. It's a corruption error as the id written in the database doesn't match the id that we calculate.
Tag for backport? |
Not if this has (as I understand it) only been broken on master? |
No need, only affects master. |
If anyone on master is affected by this, we could perhaps add a command to wallet-tool that resets the descriptor ID and cache. |
Some of my wallets are no longer opening after pulling master, and I bisected it to this commit. |
@denavila My understanding is that if you used non-release code from before this PR, then this PR will be expected to break things for you. However if you only ever run released version, this PR prevents incompatibilities. |
Yes, I was using non-release versions for these wallets. |
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.
Yes, I was using non-release versions for these wallets.
I tried to restore the signet wallet from a backup, but that also hits the same "corruption" error. Is there any other way to migrate the signet wallet to the latest code?
It's signet, so it's not the end of the world if the coins are lost, but it would still be better if I could recover them.
Probably the easiest workaround would be to revert this PR on your local repository. Compile the sources and start the app. Once you have the wallet loaded, export the descriptors by calling listdescriptors true
(showing the private keys). Then, create a new wallet on top of latest master and import the exported descriptors there.
@furszy, thanks for the workaround! I got my signet wallet restored :-) |
|
That wouldn't work. Remember that this fixed an unrecoverable corruption state The previous release would overwrite the spkm ID with the non-'h' version of it. |
…r spkm descriptor ID BACKPORT NOTICE It includes only this commit: 97a965d refactor: extract descriptor ID calculation from spkm GetID() This allows us to verify the descriptor ID on the descriptors unit tests in different software versions without requiring to use the entire DescriptorScriptPubKeyMan machinery. Note: The unit test changes are introduced after the bugfix commit but this commit + the unit test commit can be cherry-picked on top of the v25 branch to verify IDs correctness. IDs must be the same for v25 and after the bugfix commit.
…r spkm descriptor ID BACKPORT NOTICE It includes only this commit: 97a965d refactor: extract descriptor ID calculation from spkm GetID() This allows us to verify the descriptor ID on the descriptors unit tests in different software versions without requiring to use the entire DescriptorScriptPubKeyMan machinery. Note: The unit test changes are introduced after the bugfix commit but this commit + the unit test commit can be cherry-picked on top of the v25 branch to verify IDs correctness. IDs must be the same for v25 and after the bugfix commit.
…29007 - to improve CI experience 2eadcf2 partial Merge bitcoin#29007: test: create deterministic addrman in the functional tests (stratospher) f95ca4e fix: unify with bitcoin: removed requirement of txindex=0 (Konstantin Akimov) bf46e7a partial Merge bitcoin#28799: wallet: cache descriptor ID to avoid repeated descriptor string creation (Andrew Chow) 05e5966 partial Merge bitcoin#27920: wallet: bugfix, always use apostrophe for spkm descriptor ID (furszy) Pull request description: ## Issue being fixed or feature implemented Lately our CI for tsan is flapping many functional tests and take long times. This PR has several important changes backported from the latest bitcoin's version to improve CI experience ## What was done? This PR has several backports that improved CI experience drastically. **Firstly, it aims to fix flapping test p2p_node_network_limited.py** For example: https://gitlab.com/dashpay/dash/-/jobs/7692635307 <details> <summary>p2p_node_network_limited.py | ✖ Failed | 28 s</summary> ``` test 2024-08-29T02:50:53.929000Z TestFramework (ERROR): Assertion failed Traceback (most recent call last): File "/builds/dashpay/dash/build-ci/dashcore-linux64_tsan/test/functional/test_framework/test_framework.py", line 158, in main self.run_test() File "/builds/dashpay/dash/build-ci/dashcore-linux64_tsan/test/functional/p2p_node_network_limited.py", line 79, in run_test self.nodes[0].disconnect_p2ps() File "/builds/dashpay/dash/build-ci/dashcore-linux64_tsan/test/functional/test_framework/test_node.py", line 611, in disconnect_p2ps wait_until_helper(check_peers, timeout=5) File "/builds/dashpay/dash/build-ci/dashcore-linux64_tsan/test/functional/test_framework/util.py", line 262, in wait_until_helper raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout)) AssertionError: Predicate '''' def check_peers(): for p in self.getpeerinfo(): for p2p in self.p2ps: if p['subver'] == p2p.strSubVer: return False return True ''' not true after 5.0 seconds ``` </details> **Secondly, it improves performance of Descriptor wallets significantly for case of `tsan` CI**. It is tiny improvement for Release build and local runs, but some fucnctional tests run as fast as twice: https://gitlab.com/dashpay/dash/-/jobs/7694458953 https://gitlab.com/dashpay/dash/-/jobs/7665132625 ``` wallet_create_tx.py --descriptors | ✓ Passed | 236 s <-- new version wallet_create_tx.py --legacy-wallet | ✓ Passed | 108 s wallet_basic.py --descriptors | ✓ Passed | 135 s <---- new version wallet_basic.py --legacy-wallet | ✓ Passed | 97 s wallet_create_tx.py --descriptors | ✓ Passed | 456 s <-- old version wallet_create_tx.py --legacy-wallet | ✓ Passed | 98 s wallet_basic.py --descriptors | ✓ Passed | 189 s <--- old version wallet_basic.py --legacy-wallet | ✓ Passed | 131 s ``` See performance investigation here: #6226 ## How Has This Been Tested? Run unit/functional tests ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone ACKs for top commit: PastaPastaPasta: utACK 2eadcf2 UdjinM6: utACK 2eadcf2 Tree-SHA512: 127fbaa65c160aa95e2145a6b40d3811f7c42e36fbee9ce98a9ac021abd9cbe6edc7791870b331a54855ba891e3804885db7936ef212647b693f50f79a60d232
Aiming to fix #27915.
As we re-write the descriptor's db record every time that
the wallet is loaded (at
TopUp
time), if the spkm ID differsfrom the one in db, the wallet will enter in an unrecoverable
corruption state (due to the storage of a descriptor with an ID
that is not linked to any other descriptor record in DB), and
no soft version will be able to open it anymore.
Because we cannot change the past, to stay compatible between
releases, we need to always use the apostrophe version for the
spkm IDs.