-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet: bugfix, disallow migration of invalid scripts #28125
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, disallow migration of invalid scripts #28125
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. |
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
2a6825f
to
26f91a5
Compare
ACK 26f91a5 |
26f91a5
to
21ac6b2
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.
Feedback tackled, thanks achow101.
Expanded test coverage with the requested label check and few more validations. Now the test:
- Verifies that neither the invalid double sh script label, nor the
addr(<script_hash_addr>)
descriptor, are contained by any of the migrated wallets. - Verifies that the valid sh script (the original single sh script that is imported at the same time that the invalid one) and its address book record are contained by the migrated watch-only wallet.
Also, while was doing this. Corrected another behavior; the invalid script label was being retained in the main wallet (not on the watch-only one), which was not intended.
@MarcoFalke does this fix #28057 in it's current state? |
It did fix the crash when I left the Concept ACK #28125 (review) Happy to re-test after the next ACK or after merge. |
src/wallet/wallet.cpp
Outdated
bool skip_record = false; | ||
for (const auto& script : not_migrated_scripts) { | ||
CTxDestination dest; | ||
ExtractDestination(script, dest); | ||
if (IsValidDestination(dest) && dest == addr_pair.first) { | ||
skip_record = true; | ||
dests_to_delete.push_back(addr_pair.first); | ||
break; | ||
} | ||
} | ||
if (skip_record) continue; // Skip record linked to a script that we will not migrate |
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.
In 0112666 "wallet: disallow migration of invalid or not-watched scripts"
Instead of iterating over not_migrated_scripts
for every address book record that gets here, we could instead convert the address book destination to a script and just do a lookup in the set.
bool skip_record = false; | |
for (const auto& script : not_migrated_scripts) { | |
CTxDestination dest; | |
ExtractDestination(script, dest); | |
if (IsValidDestination(dest) && dest == addr_pair.first) { | |
skip_record = true; | |
dests_to_delete.push_back(addr_pair.first); | |
break; | |
} | |
} | |
if (skip_record) continue; // Skip record linked to a script that we will not migrate | |
CScript addr_script = GetScriptForDestination(addr_pair.first); | |
if (!addr_script.empty() && not_migrated_scripts.count(script) > 0) { | |
dests_to_delete.push_back(addr_pair.first); | |
continue; | |
} |
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.
In 0112666 "wallet: disallow migration of invalid or not-watched scripts"
Instead of iterating over
not_migrated_scripts
for every address book record that gets here, we could instead convert the address book destination to a script and just do a lookup in the set.
I don't think that will work for p2pk scripts.
We store the p2pk script as PKHash
destinations inside the address book. So, by calling GetScriptForDestination()
, the resulting script will be a p2pkh. Which will not match the watched p2pk inside not_migrated_scripts
.
With the current ExtractDestination()
call, we are going in the other way around: we convert the p2pk script into a PKHash
destination, which matches the destination stored in the address book.
What could do is create a not_migrated_destinations
set from the not_migrated_scripts
outside of the loop (by using the ExtractDestination()
call), and use it here.
EDIT: Squashed this last point inside 1de8a23. Code should be a bit more readable now.
The legacy wallet allowed to import any raw script, without checking if it was valid or not. Appending it to the watch-only set. This causes a crash in the migration process because we are only expecting to find valid scripts inside the legacy spkm. These stored scripts internally map to `ISMINE_NO` (same as if they weren't stored at all..). So we need to check for these special case, and take into account that the legacy spkm could be storing invalid not watched scripts. Which, in code words, means IsMineInner() returning IsMineResult::INVALID for them.
The migration process must skip any invalid script inside the legacy spkm and all the addressbook records linked to them. These scripts are not being watched by the current wallet, nor should be watched by the migrated one. IsMine() returns ISMINE_NO for them.
21ac6b2
to
8e7e3e6
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.
Updated per feedback. Thanks achow.
src/wallet/wallet.cpp
Outdated
bool skip_record = false; | ||
for (const auto& script : not_migrated_scripts) { | ||
CTxDestination dest; | ||
ExtractDestination(script, dest); | ||
if (IsValidDestination(dest) && dest == addr_pair.first) { | ||
skip_record = true; | ||
dests_to_delete.push_back(addr_pair.first); | ||
break; | ||
} | ||
} | ||
if (skip_record) continue; // Skip record linked to a script that we will not migrate |
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.
In 0112666 "wallet: disallow migration of invalid or not-watched scripts"
Instead of iterating over
not_migrated_scripts
for every address book record that gets here, we could instead convert the address book destination to a script and just do a lookup in the set.
I don't think that will work for p2pk scripts.
We store the p2pk script as PKHash
destinations inside the address book. So, by calling GetScriptForDestination()
, the resulting script will be a p2pkh. Which will not match the watched p2pk inside not_migrated_scripts
.
With the current ExtractDestination()
call, we are going in the other way around: we convert the p2pk script into a PKHash
destination, which matches the destination stored in the address book.
What could do is create a not_migrated_destinations
set from the not_migrated_scripts
outside of the loop (by using the ExtractDestination()
call), and use it here.
EDIT: Squashed this last point inside 1de8a23. Code should be a bit more readable now.
ACK 8e7e3e6 |
re-checked that this no longer crashes my wallet.dat on |
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.
Ok
The legacy wallet allowed to import any raw script, without checking if it was valid or not. Appending it to the watch-only set. This causes a crash in the migration process because we are only expecting to find valid scripts inside the legacy spkm. These stored scripts internally map to `ISMINE_NO` (same as if they weren't stored at all..). So we need to check for these special case, and take into account that the legacy spkm could be storing invalid not watched scripts. Which, in code words, means IsMineInner() returning IsMineResult::INVALID for them. Github-Pull: bitcoin#28125 Rebased-From: 1de8a23
The migration process must skip any invalid script inside the legacy spkm and all the addressbook records linked to them. These scripts are not being watched by the current wallet, nor should be watched by the migrated one. IsMine() returns ISMINE_NO for them. Github-Pull: bitcoin#28125 Rebased-From: 8e7e3e6
Backported to 25.x in #28487. |
…ripts 8e7e3e6 test: wallet, verify migration doesn't crash for an invalid script (furszy) 1de8a23 wallet: disallow migration of invalid or not-watched scripts (furszy) Pull request description: Fixing bitcoin#28057. The legacy wallet allows to import any raw script (bitcoin#28126), without checking if it was valid or not. Appending it to the watch-only set. This causes a crash in the migration process because we are only expecting to find valid scripts inside the legacy spkm. These stored scripts internally map to `ISMINE_NO` (same as if they weren't stored at all..). So we need to check for these special case, and take into account that the legacy spkm could be storing invalid not watched scripts. Which, in code words, means `IsMineInner()` returning `IsMineResult::INVALID` for them. Note: To verify this, can run the test commit on top of master. `wallet_migration.py` will crash without the bugfix commit. ACKs for top commit: achow101: ACK 8e7e3e6 Tree-SHA512: c2070e8ba78037a8f573b05bf6caa672803188f05429adf5b93f9fc1493faedadecdf018dee9ead27c656710558c849c5da8ca5f6f3bc9c23b3c4275d2fb50c7
…ripts 8e7e3e6 test: wallet, verify migration doesn't crash for an invalid script (furszy) 1de8a23 wallet: disallow migration of invalid or not-watched scripts (furszy) Pull request description: Fixing bitcoin#28057. The legacy wallet allows to import any raw script (bitcoin#28126), without checking if it was valid or not. Appending it to the watch-only set. This causes a crash in the migration process because we are only expecting to find valid scripts inside the legacy spkm. These stored scripts internally map to `ISMINE_NO` (same as if they weren't stored at all..). So we need to check for these special case, and take into account that the legacy spkm could be storing invalid not watched scripts. Which, in code words, means `IsMineInner()` returning `IsMineResult::INVALID` for them. Note: To verify this, can run the test commit on top of master. `wallet_migration.py` will crash without the bugfix commit. ACKs for top commit: achow101: ACK 8e7e3e6 Tree-SHA512: c2070e8ba78037a8f573b05bf6caa672803188f05429adf5b93f9fc1493faedadecdf018dee9ead27c656710558c849c5da8ca5f6f3bc9c23b3c4275d2fb50c7
The legacy wallet allowed to import any raw script, without checking if it was valid or not. Appending it to the watch-only set. This causes a crash in the migration process because we are only expecting to find valid scripts inside the legacy spkm. These stored scripts internally map to `ISMINE_NO` (same as if they weren't stored at all..). So we need to check for these special case, and take into account that the legacy spkm could be storing invalid not watched scripts. Which, in code words, means IsMineInner() returning IsMineResult::INVALID for them. Github-Pull: bitcoin#28125 Rebased-From: 1de8a23
The migration process must skip any invalid script inside the legacy spkm and all the addressbook records linked to them. These scripts are not being watched by the current wallet, nor should be watched by the migrated one. IsMine() returns ISMINE_NO for them. Github-Pull: bitcoin#28125 Rebased-From: 8e7e3e6
45a5fcb http: bugfix: track closed connection (stickies-v) 752a456 http: log connection instead of request count (stickies-v) ae86ada http: refactor: use encapsulated HTTPRequestTracker (stickies-v) f31899d gui: macOS, make appMenuBar part of the main app window (furszy) 64ffa94 gui: macOS, do not process dock icon actions during shutdown (furszy) e270f3f depends: fix unusable memory_resource in macos qt build (fanquake) a668394 build, macos: Fix `qt` package build with new Xcode 15 linker (Hennadii Stepanov) b3517cb test: Test loading wallets with conflicts without a chain (Andrew Chow) d63478c wallet: Check last block and conflict height are valid in MarkConflicted (Andrew Chow) 5e51a9c ci: Nuke Android APK task, Use credits for tsan (MarcoFalke) 910c362 test: ensure old fee_estimate.dat not read on restart and flushed (ismaelsadeeq) 37764d3 tx fees, policy: read stale fee estimates with a regtest-only option (ismaelsadeeq) 16bb916 tx fees, policy: do not read estimates of old fee_estimates.dat (ismaelsadeeq) c4dd598 tx fees, policy: periodically flush fee estimates to fee_estimates.dat (ismaelsadeeq) c36770c test: wallet, verify migration doesn't crash for an invalid script (furszy) 0d2a33e wallet: disallow migration of invalid or not-watched scripts (furszy) 2c51a07 Do not use std::vector = {} to release memory (Pieter Wuille) Pull request description: Further backports for the `25.x` branch. Currently: * #27622 * #27834 * #28125 * #28452 * #28542 * #28543 * #28551 * #28571 * bitcoin-core/gui#751 ACKs for top commit: hebasto: re-ACK 45a5fcb, only #28551 has been backported with since my recent [review](#28487 (review)). dergoegge: reACK 45a5fcb willcl-ark: reACK 45a5fcb Tree-SHA512: 0f5807aa364b7c2a2039fef11d5cd5e168372c3bf5b0e941350fcd92e7db4a1662801b97bb4f68e29788c77d24bbf97385a483c4501ca72d93fa25327d5694fa
Fixing #28057.
The legacy wallet allows to import any raw script (#28126), without
checking if it was valid or not. Appending it to the watch-only set.
This causes a crash in the migration process because we are only
expecting to find valid scripts inside the legacy spkm.
These stored scripts internally map to
ISMINE_NO
(same as if theyweren't stored at all..).
So we need to check for these special case, and take into account that
the legacy spkm could be storing invalid not watched scripts.
Which, in code words, means
IsMineInner()
returningIsMineResult::INVALID
for them.Note:
To verify this, can run the test commit on top of master.
wallet_migration.py
will crash without the bugfix commit.