-
Notifications
You must be signed in to change notification settings - Fork 36.5k
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: fix crash during watch-only wallet migration #31374
wallet: fix crash during watch-only wallet migration #31374
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/31374. 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. |
982ce98
to
ea9528f
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. |
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.
0d5bf47
to
b63cd86
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.
LGTM, will run the fuzz test from #29694 on top during the night and ACK tomorrow if there's no crash. Only left some test nits below.
97ddf4a
to
8c68ba5
Compare
ACK 8c68ba5 |
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.
ACK 8c68ba5
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.
ACK 8c68ba5
The crash occurs because we assume the cached scripts structure will not be empty, but it can be empty when the legacy wallet contained only watch-only and solvable but not spendable scripts
8c68ba5
to
080e7ec
Compare
Rebased due to a silent conflict with the recently merged #31248. The tests now use the previous release node to create the wallet and the latest version node to migrate it. Ready to go. |
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.
reACK 080e7ec
080e7ec
to
cdd207c
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.
re-ACK cdd207c
Left two nits (not worth to tackle if you don't need to retouch for other reasons imho).
res, _ = self.migrate_and_get_rpc("bare_p2pk") | ||
wo_wallet = self.master_node.get_wallet_rpc(res['watchonly_name']) | ||
assert_equal(wo_wallet.listdescriptors()['descriptors'][0]['desc'], descsum_create(f'pk({pubkey.hex()})')) | ||
wo_wallet.unloadwallet() |
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.
nit: could still unload the migrated wallet here by using the second return value of migrate_and_get_rpc
(but OTOH, the unloading is not done consistently in other sub-tests anyway, so maybe material for a different PR...)
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.
nit: could still unload the migrated wallet here by using the second return value of
migrate_and_get_rpc
(but OTOH, the unloading is not done consistently in other sub-tests anyway, so maybe material for a different PR...)
Actually, I didn't unload it on purpose. Aligned this PR to #31423 expected outcome.
Short description: This test creates a blank legacy wallet, imports a watch-only P2PK script, and executes migration --> This scenario shouldn't create a brand-new spendable wallet as it is doing right now. The pre-migration wallet is a watch-only wallet with no key material.
self.log.info("Test migrating standalone private keys") | ||
wallet = self.create_legacy_wallet("import_privkeys", blank=True) | ||
privkey, pubkey = generate_keypair(wif=True) | ||
wallet.importprivkey(privkey=privkey, label="hi", rescan=False) |
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.
nit, seems there is no need for a label here
wallet.importprivkey(privkey=privkey, label="hi", rescan=False) | |
wallet.importprivkey(privkey=privkey, rescan=False) |
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.
reACK cdd207c
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.
ACK cdd207c
pk_desc = descsum_create(f'pk([{key_origin}]{pubkey_hex})') | ||
pkh_desc = descsum_create(f'pkh([{key_origin}]{pubkey_hex})') | ||
sh_wpkh_desc = descsum_create(f'sh(wpkh([{key_origin}]{pubkey_hex}))') | ||
wpkh_desc = descsum_create(f'wpkh([{key_origin}]{pubkey_hex})') | ||
expected_descs = [pk_desc, pkh_desc, sh_wpkh_desc, wpkh_desc] |
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.
It was surprising to me that the migrated wallet would make all of these individual descriptors when it could make a single comb()
. Further investigation reveals that there is a minor bug in the handling of non-HD keys that would result in the separate descriptors rather than the combo()
.
Semantically, these 4 descriptors are the same as combo()
, so this would never have caused any funds loss, however it is unexpected and should be fixed in a followup.
pk_desc = descsum_create(f'pk({pubkey_hex})') | ||
pkh_desc = descsum_create(f'pkh({pubkey_hex})') | ||
sh_wpkh_desc = descsum_create(f'sh(wpkh({pubkey_hex}))') | ||
wpkh_desc = descsum_create(f'wpkh({pubkey_hex})') | ||
expected_descs = [pk_desc, pkh_desc, sh_wpkh_desc, wpkh_desc] |
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.
Similar to the above, however this is more expected since watchonly pubkeys are not being specially treated to produce combo()
descriptors, but we should probably make combo()
for them as well.
Edit: On further thought, this appears to not be as possible since we can have watchonly pubkeys but not be watching all of the combo()
output scripts for them.
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.
Edit: On further thought, this appears to not be as possible since we can have watchonly pubkeys but not be watching all of the combo() output scripts for them.
Not sure how this could be possible, but I think it would be better to add the combo and track more scripts instead of fewer. Importing a private key and a public key should behave the same at the "scan for X level". The only difference should be that one lets you spend the script, while the other doesn't.
nvm, just checked the code. importpubkey
does not save the key to disk. Only scripts created from the pubkey are stored.
…tion cdd207c test: add coverage for migrating standalone imported keys (furszy) 297a876 test: add coverage for migrating watch-only script (furszy) 932cd1e wallet: fix crash during watch-only wallet migration (furszy) Pull request description: The crash occurs because we assume the cached scripts structure will not be empty, but it can be empty for watch-only wallets that start blank. This also adds test coverage for standalone imported keys, which were also crashing because pubkey imports are treated the same way as hex script imports through `importaddress()`. Testing Notes: This can be verified by cherry-picking and running any of the test commits on master. It will crash there but pass on this branch.
The crash occurs because we assume the cached scripts structure will not be empty, but it can be empty when the legacy wallet contained only watch-only and solvable but not spendable scripts Github-Pull: bitcoin#31374 Rebased-From: 932cd1e
Github-Pull: bitcoin#31374 Rebased-From: 297a876
Github-Pull: bitcoin#31374 Rebased-From: cdd207c
62b2d23 wallet: Migrate non-HD keys to combo() descriptor (Ava Chow) Pull request description: Non-HD keys do not have an HD seed ID associated with them, so if this value is the null value (all 0s), then we should not perform any seed ID comparison that would result in excluding the keys from combo() migration. This changes the migration of non-HD wallets (or blank wallets with imported private keys) to make a single combo() descriptors for the non-HD/imported keys, rather than pk(), pkh(), sh(wpkh()), and wpkh() descriptors for the keys. Implements #31374 (comment) ACKs for top commit: laanwj: Concept and code review ACK 62b2d23 brunoerg: code review ACK 62b2d23 furszy: Nice catch. ACK 62b2d23 theStack: ACK 62b2d23 rkrux: tACK 62b2d23 Tree-SHA512: 86a80b7dcc1598ab18068a2572ff4b4920b233178b760f7b76c5b21a9e6608005ac872f90e082a8f99b51daab0b049e73e4bee5b8e0b537d56ed0d34122a1f49
Removed the label as this isn't being backported. |
The crash occurs because we assume the cached scripts structure will not be empty,
but it can be empty for watch-only wallets that start blank.
This also adds test coverage for standalone imported keys, which were also crashing
because pubkey imports are treated the same way as hex script imports through
importaddress()
.Testing Notes:
This can be verified by cherry-picking and running any of the test commits on master.
It will crash there but pass on this branch.