-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet: Check for uninitialized last processed and conflicting heights in MarkConflicted #28542
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
MarkConflicted calculates conflict confirmations incorrectly when both the last block processed height and the conflicting height are negative (i.e. uninitialized). If either are negative, we should not be marking conflicts and should exit early.
Loading a wallet with conflicts without a chain (e.g. wallet tool and migration) would previously result in an assertion due to -1 being both a valid number of conflict confirmations, and the indicator that that member has not been set yet.
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. |
From CI https://cirrus-ci.com/task/4777376270254080?logs=ci#L2730:
|
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.
Code review ACK 782701c. Nice catch, and clever test (grinding the txid)
Can't reproduce the CI failure. |
10 re-runs were scheduled. Let's see what happens. |
// If number of conflict confirms cannot be determined, this means | ||
// that the block is still unknown or not yet part of the main chain, | ||
// for example when loading the wallet during a reindex. Do nothing in that | ||
// case. | ||
if (m_last_block_processed_height < 0 || conflicting_height < 0) { |
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.
Do you know when conflicting_height
can be negative? It looks like a different bug if that happens.
As far as I can see, we only set the TxStateConflicted
state at AddToWalletIfInvolvingMe
, where confirmed_block_height
is always positive. In LoadToWallet
, we take the conflicting height of the prev tx, so it should never be negative.
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.
When the CWalletTx
is loaded, it only contains the hash of conflicting block. The height will be set to -1. LoadToWallet
will try to lookup the block by hash, but if there is not chain available (as with the wallet tool and during migration), then it cannot do that. So the height will remain -1. If that transaction has a child, and that child is loaded after the parent is loaded, then MarkConflicted
will be called using the parent's in-memory conflicted state. Since there was no chain, the conflicting_height
that is passed to MarkConflicted
is -1, hence triggering this error.
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 782701c
Looks like tsan CI passed 10 times. I've also run the rpc on more random files I found somewhere, but I haven't reviewed the code. Concept ACK. |
Looks like we should backport this as well? |
Added to #28487 for backporting into 25.x. |
MarkConflicted calculates conflict confirmations incorrectly when both the last block processed height and the conflicting height are negative (i.e. uninitialized). If either are negative, we should not be marking conflicts and should exit early. Github-Pull: bitcoin#28542 Rebased-From: 4660fc8
Loading a wallet with conflicts without a chain (e.g. wallet tool and migration) would previously result in an assertion due to -1 being both a valid number of conflict confirmations, and the indicator that that member has not been set yet. Github-Pull: bitcoin#28542 Rebased-From: 782701c
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
…nd conflicting heights in MarkConflicted 782701c test: Test loading wallets with conflicts without a chain (Andrew Chow) 4660fc8 wallet: Check last block and conflict height are valid in MarkConflicted (Andrew Chow) Pull request description: `MarkConflicted` assumes that `m_last_block_processed_height` is always valid. However it may not be valid when a chain is not attached, as happens in the wallet tool and during migration. In such situations, when the conflicting height is also negative (which occurs on loading when no chain is available), the calculation of the number of conflict confirms results in a non-negative value which passes the existing check for valid values. This will subsequently hit an assertion in `GetTxDepthInMainChain`. Furthermore, `MarkConflicted` is also only called on loading a transaction whose parent has a stored state of `TxStateConflicted` and was loaded before the child transaction. This depends on the loading order, which for both sqlite and bdb depends on the txids. We can avoid this by explicitly checking that both `m_last_block_processed_height` and `conflicting_height` are non-negative. Both `tool_wallet.py` and `wallet_migration.py` are updated to create wallets with a state that triggers the assertion. Fixes bitcoin#28510 ACKs for top commit: ryanofsky: Code review ACK 782701c. Nice catch, and clever test (grinding the txid) furszy: ACK 782701c Tree-SHA512: 1344e0279ec5413a43a2819d101fb571fbf4821de2d13958a0fdffc99f57082ef3243ec454c8343f97dc02ed1fce8c8b0fd89388420ab2e55618af42ad5630a9
MarkConflicted
assumes thatm_last_block_processed_height
is always valid. However it may not be valid when a chain is not attached, as happens in the wallet tool and during migration. In such situations, when the conflicting height is also negative (which occurs on loading when no chain is available), the calculation of the number of conflict confirms results in a non-negative value which passes the existing check for valid values. This will subsequently hit an assertion inGetTxDepthInMainChain
.Furthermore,
MarkConflicted
is also only called on loading a transaction whose parent has a stored state ofTxStateConflicted
and was loaded before the child transaction. This depends on the loading order, which for both sqlite and bdb depends on the txids.We can avoid this by explicitly checking that both
m_last_block_processed_height
andconflicting_height
are non-negative. Bothtool_wallet.py
andwallet_migration.py
are updated to create wallets with a state that triggers the assertion.Fixes #28510