-
Notifications
You must be signed in to change notification settings - Fork 38.7k
bitcoin-wallet salvage: Return false instead of asserting when a loaded tx isn't new #19793
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
Conversation
Returning false will give us a recoverable error instead of killing the node entirely. In that situation, the wallet is still recoverable by deleting all txs and rescanning. Co-authored-by: Russell Yanofsky <[email protected]>
maflcko
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 on removing an assert that should only be used for code internal logic errors and not user/disk input validation
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.
Updated 8e734a1 -> c4deddb (pr/badsalv.1 -> pr/badsalv.2, compare) just tweaking the comment.
|
ACK c4deddb |
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.
utACK c4deddb
Very sorry I missed this, nice simple PR shouldn't take over a month to review 😅
| if (wss.corrupt || IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) { | ||
| result = DBErrors::CORRUPT; | ||
| } else if (strType == DBKeys::FLAGS) { | ||
| // reading the wallet flags can only fail if unknown flags are present |
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.
should the other corruption reasons also latch to true?
wss.corrupt would never be set to false, so the error TOO_NEW is translated to the error CORRUPT (after at least one CORRUPT error).
With a lost key this doesn't seem to happen and the error might be rewritten to TOO_NEW.
Probably doesn't matter in practice, but I wanted to ask to be sure.
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: #19793 (comment)
should the other corruption reasons also latch to true?
wss.corruptwould never be set to false, so the errorTOO_NEWis translated to the errorCORRUPT(after at least one CORRUPT error).With a lost key this doesn't seem to happen and the error might be rewritten to TOO_NEW.
Probably doesn't matter in practice, but I wanted to ask to be sure.
Looking at the calling code, in practice the wallet will fail to load either way if TOO_NEW or CORRUPT is returned, only the error text will be different. It's not even a new thing for the CORRUPT error to take priority over the TOO_NEW error since it can also happen in other the places result = DBErrors::CORRUPT is set below.
But one somewhat ugly side effect of this is it can make gArgs.SoftSetBoolArg("-rescan", true) line not get called consistency. It would really be better not to do this and for rescan status were passed back more directly, but that requires more changes outside the file.
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.
Closing this since it's out of date and has some odd behaviors. Things it would be nice to do if someone wants to clean this code up:
- Replace assert(new_tx) with error handling that triggers the
CWallet::Create"Wallet Corrupted" error - Fix the "This error is recoverable with zapwallettxs" comment now that zapwallettxs is removed.
- Make the TOO_NEW error take precedence over the CORRUPT error or enforce some other consistent precedence
- Replace
SoftSetBoolArg("-rescan", true)with arescan_requiredreturn value so one wallet requiring a rescan doesn't cause other wallets to be rescanned
| if (wss.corrupt || IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) { | ||
| result = DBErrors::CORRUPT; | ||
| } else if (strType == DBKeys::FLAGS) { | ||
| // reading the wallet flags can only fail if unknown flags are present |
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: #19793 (comment)
should the other corruption reasons also latch to true?
wss.corruptwould never be set to false, so the errorTOO_NEWis translated to the errorCORRUPT(after at least one CORRUPT error).With a lost key this doesn't seem to happen and the error might be rewritten to TOO_NEW.
Probably doesn't matter in practice, but I wanted to ask to be sure.
Looking at the calling code, in practice the wallet will fail to load either way if TOO_NEW or CORRUPT is returned, only the error text will be different. It's not even a new thing for the CORRUPT error to take priority over the TOO_NEW error since it can also happen in other the places result = DBErrors::CORRUPT is set below.
But one somewhat ugly side effect of this is it can make gArgs.SoftSetBoolArg("-rescan", true) line not get called consistency. It would really be better not to do this and for rescan status were passed back more directly, but that requires more changes outside the file.
| // losing keys is considered a catastrophic error, anything else | ||
| // we assume the user can live with: | ||
| if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) { | ||
| if (wss.corrupt || IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) { |
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.
A corrupt/missing transaction isn't supposed to follow this path, but rather the one below which simply triggers a rescan.
1-3 seems to be handled by #23142 and 4 seems to be handled by #23123 |
0ab4c3b Return false on corrupt tx rather than asserting (Samuel Dobson) Pull request description: Takes up #19793 Rather than asserting, we log an error and return CORRUPT so that the user is informed. This type of error isn't critical so it isn't worth `assert`ing. ACKs for top commit: achow101: ACK 0ab4c3b laanwj: Code review ACK 0ab4c3b ryanofsky: Code review ACK 0ab4c3b. There may be room for more improvements later like better error messages or easier recovery options, but changing from an assert to an error seems like a clear improvement, and this seems to avoid all the pitfalls of the last PR that tried this. Tree-SHA512: 4a1a412e7c473d176c4e09123b85f390a6b0ea195e78d28ebd50b13814b7852f8225a172511a2efb6affb555b11bd4e667c19eb8c78b060c5444b62f0fae5f7a
0ab4c3b Return false on corrupt tx rather than asserting (Samuel Dobson) Pull request description: Takes up bitcoin#19793 Rather than asserting, we log an error and return CORRUPT so that the user is informed. This type of error isn't critical so it isn't worth `assert`ing. ACKs for top commit: achow101: ACK 0ab4c3b laanwj: Code review ACK 0ab4c3b ryanofsky: Code review ACK 0ab4c3b. There may be room for more improvements later like better error messages or easier recovery options, but changing from an assert to an error seems like a clear improvement, and this seems to avoid all the pitfalls of the last PR that tried this. Tree-SHA512: 4a1a412e7c473d176c4e09123b85f390a6b0ea195e78d28ebd50b13814b7852f8225a172511a2efb6affb555b11bd4e667c19eb8c78b060c5444b62f0fae5f7a
Returning false will give us a recoverable error instead of killing the node entirely. In that situation, the wallet is still recoverable by deleting all txs and rescanning.
This commit was originally part of #19457.
Original motivation for this change was to avoid crashes with #19078, but a better fix for that problem is in #19793. This change is still beneficial because it removes an unsafe assert.