Repository navigation
Keep stored hash when invalidating a transaction - #720
Merged
Merged
Conversation
A conflicting transaction that spends the same outpoint with the same sequence replaces the stored input row of the original. Rebuilding the original from stored rows then rehashed it to a different hash, and the invalidator saved the InvalidTransaction under that hash, which has no TransactionMetadata, and left the original row in place. Loading the transaction list then threw NoSuchElementException. Transactions loaded from storage now keep their stored hash, the list tolerates a missing metadata row, and migration 19 to 20 moves already misfiled invalid transactions back to their real hash by uid and removes the leftover original.
omurovch
approved these changes
Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the wallet crash
NoSuchElementExceptioninStorage.getFullTransactionInfowhen the transaction list loads.Why this approach: the stored hash is authoritative. Rebuilding a stored transaction rehashed it from stored input rows, which a same-outpoint, same-sequence conflict can replace, so the invalidator filed the
InvalidTransactionunder a hash with no metadata and left the original row behind. Falling back on missing metadata alone would hide the crash but keep the duplicate pending copy, so the migration also finishes the interrupted move.How to verify:
./gradlew :bitcoincore:connectedDebugAndroidTeston an emulator.InvalidTransactionMetadataTestreproduces the crash on the previous code, builds the broken v19 state and checks that the migration repairs it.Risky:
Migration_19_20matches the misfiled row to the original byuid; onlyInvalidTransaction's constructor copies a uid, so a uid in both tables comes only from this bug. Input rows are still keyed withouttransactionHash, so conflicting transactions can still take over each other's input rows; that is left for a separate change.