wallet: encapsulate transactions state#16624
Conversation
3f2eb1d to
0e3212a
Compare
|
Travis is stalling yet another time.. |
|
Concept ACK |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. 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. |
|
Concept ACK. This overlaps (but is compatible) with #9381 |
src/wallet/wallet.h
Outdated
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Why is the default initialization changed to 0?
There was a problem hiding this comment.
Why is the default initialization changed to 0?
From what I can tell in this PR, the convention mostly adhered to in this PR is to stop using -1 and ABANDON_HASH values at runtime, and restrict them only to the serialization code. This seems like a pretty good convention if it's followed consistently. I also think it'd be nice to explicitly say what values the block and index values should have for different states in a comment.
There was a problem hiding this comment.
Yes see comment on top of TxConfirmation, should be improved ?
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Can we move this backwards compatibility feature entirely to the serialization code, and then make nIndex a uint (for clarity)?
There was a problem hiding this comment.
Can we move this backwards compatibility feature entirely to the serialization code, and then make
nIndexauint(for clarity)?
Personally -0 on this. Imo, unsigned types provide no safety and are terrible for everything except bitfields.
There was a problem hiding this comment.
What do you mean here by moving this backwards compatibility feature entirely to the serialization code ? If changes are right this logic should only lived in serialization code now.
There was a problem hiding this comment.
See below, it depends on if nIndex has any meaning.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Mention that nIndex is a block height?
There was a problem hiding this comment.
With these changes, but even before, nIndex never referred directly to a block height. When set at -1, it's interpreted as a magic value meaning the hashBlock is one of the deepest conflicting tx. This PR intents to scope this logic only to serialization/deserialization.
There was a problem hiding this comment.
If nIndex doesn't mean a block height, then why not remove the instance variable altogether? We can still (de)serialize in a backwards compatible way.
There was a problem hiding this comment.
Well I think we need to keep nIndex at least it's used by RPCs in WalletTxToJSON ? Or getting it out of new TxConfirmation struct ?
There was a problem hiding this comment.
I think we should figure out what it means in the RPC, consider deprecating that field, and then having an explicit backwards compatible workaround.
In the backwards compatiblity tests I wrote it seems blockindex is absent for uncofirmned or abandoned transactions. It was 1 for a confirmed transaction.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
isConfirmed() does not seem to be tested as the others. Add a test? :-)
There was a problem hiding this comment.
Removed for now, I've added it for further changes were ABANDON state was dual-set with other states, but drop if for now, would have been too big changes at once.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Should be "conflicted"? :-)
ryanofsky
left a comment
There was a problem hiding this comment.
Some questions, but this looks very good and is a welcome change that should make code more readable.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
if hashBlock is null it means transaction is abandoned
Could you clarify this part of the comment. Doesn't 1 not 0 mean abandoned?
There was a problem hiding this comment.
Currently, AbandonTransaction set nIndex as -1 and this value is kept at serialization/deserialization. I'm not sure if it make sense for older client, because an abandoned transaction can be also confirmed/unconfirmed/conflicted at same time. Nevertheless, I've tried to keep same behavior for now, so hashBlock set as ABANDON_HASH is used to avoid deserialization ambiguity with conflicted.
I've clarified this comment, the ABANDON_HASH one and also reset abandon txn hashBlock at deserialization. ABANDON_HASH should be scoped at serialization/deserialization.
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
Comment above about "if transaction was found in a block" no longer makes sense without the if condition.
Also, it seems like now when an abandoned, conflicted, or confirmed transaction is added to the mempool, and this is called, the transaction will now be marked unconfirmed instead of left in the previous state.
This seems logical, but I'm wondering if it is a change in behavior. It'd be helpful if the PR description or commit message would say explicitly whether any of this changes wallet behavior, and what the changes are if it does.
There was a problem hiding this comment.
Clarified in commit message that is a change in behavior where at block disconnection, previous tx state is override by UNCONFIRMED one. I think right now, we don't track at all the fact that tx has been disconnected, and that's why we don't undo conflicts, or make it hard to solve them. The only direct consequence of these changes I can think of is a user having to call abandontransaction a 2nd time if block get disconnected and transaction is not back in mempool.
This change would make easier to track conflicts, where if conflicting transaction get UNCONFIRMED, iterate on conflicted outpoints and make them free.
Maybe, I can avoid update right now for UNCONFIRMED state, so a tx is born in this state but never go backward to it, and save implications for a later PR ?
There was a problem hiding this comment.
I can live with changing it now, but we should drop the comment then.
There was a problem hiding this comment.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26dc3c69e219a2db8d52980074951aff55a3)
On the surface it seems like improved behavior to not consider a transaction conflicted or abandoned when it's in the mempool. So just removing or updating the comment above seems good for that case. If this can mark a transaction not abandoned when it's not actually in the mempool that also seems ok but might be worth noting in a comment.
Ideally this commit might just be a pure refactor, and behavior changes could be left for other commits or PRs, but I think it's fine to have changes here if they're explicitly noted.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Can we move this backwards compatibility feature entirely to the serialization code, and then make
nIndexauint(for clarity)?
Personally -0 on this. Imo, unsigned types provide no safety and are terrible for everything except bitfields.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Just a suggestion, but I'd add = 0 here and = UNCONFIRMED on the line below to simplify constructor and deserialization code.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Why is the default initialization changed to 0?
From what I can tell in this PR, the convention mostly adhered to in this PR is to stop using -1 and ABANDON_HASH values at runtime, and restrict them only to the serialization code. This seems like a pretty good convention if it's followed consistently. I also think it'd be nice to explicitly say what values the block and index values should have for different states in a comment.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Can we set 0 here, or change MarkConflicted to set -1? I think it'd be good to be consistent about what conflicted transactions look like.
There was a problem hiding this comment.
You're right that an inconsistency from my part. Corrected.
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
In commit "Remove SyncTransaction for conflicted txn in CWallet::BlockConnected" (0e3212ae85c7cf1eb6a8d937ac67d6822f624175)
I don't think I understand this change. Commit description suggests why it is ok, but I don't understand what motivates it. Is it just cleanup? Is it maybe a minor bugfix, or needed for a future change?
There was a problem hiding this comment.
Updated commit message. I think this redundant, as conflicts tagging logic is triggered by connection of conflicting transaction. AFAIK, it's not a bugfix but a cleanup. And as it was same issue than major commit, I bundled them together.
There was a problem hiding this comment.
Can you clarify why we keep the first loop at all? Why not call TransactionRemovedFromMempool before SyncTransaction in the second loop?
There was a problem hiding this comment.
Extended commit message, we keep the loop because set of conflicted txn isn't same as txn included in a block.
0e3212a to
ba86976
Compare
|
Thanks all for reviews, updated with comments corrected at 6cc34fc. |
ba86976 to
6cc34fc
Compare
|
Concept ACK |
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
Can you clarify why we keep the first loop at all? Why not call TransactionRemovedFromMempool before SyncTransaction in the second loop?
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Above this comment: For a CONFIRMED transaction, nIndex is the position inside the block, used in merkle proofs.
This had me confused before; I assumed it referred to a block height.
This serialization comment can be moved to the serialization code, because with this change nIndex is never -1; only serializedIndex is.
There was a problem hiding this comment.
Moved to the deserialization code!
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
TxConfirmation tx_state is now a confusingly named variable.
There was a problem hiding this comment.
Oh I've struggled hard on naming, TxConfirmation is struct with tx state + its values (block hash, position in block). If you have any better names, I'll take it :)
There was a problem hiding this comment.
The variable name tx_state is the confusing part, because it's not a TxState
There was a problem hiding this comment.
The variable name
tx_stateis the confusing part, because it's not a TxState
I think the current naming is ok, but if it I had to choose, I would probably do something like:
class CWalletTx
{
public:
enum Status { UNCONFIRMED, CONFIRMED, CONFLICTED, ABANDONED };
struct Confirmation {
Status status;
uint256 block_hash
int pos_in_block;
}
...
Confirmation m_confirm;
...
};There was a problem hiding this comment.
Replaced by tx_conf on f9f4926
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
I can live with changing it now, but we should drop the comment then.
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
posInBlock could be a good rename candidate for nIndex, though probably not worth making the diff bigger.
6cc34fc to
3e027c7
Compare
|
Thanks @Sjors for review, pushed some corrections on de4459c |
de4459c to
f9f4926
Compare
ryanofsky
left a comment
There was a problem hiding this comment.
utACK 3e027c776556c3070a9c3460045a6e397b21ceb8
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26dc3c69e219a2db8d52980074951aff55a3)
On the surface it seems like improved behavior to not consider a transaction conflicted or abandoned when it's in the mempool. So just removing or updating the comment above seems good for that case. If this can mark a transaction not abandoned when it's not actually in the mempool that also seems ok but might be worth noting in a comment.
Ideally this commit might just be a pure refactor, and behavior changes could be left for other commits or PRs, but I think it's fine to have changes here if they're explicitly noted.
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26dc3c69e219a2db8d52980074951aff55a3)
Should we maybe add
} else {
assert(wtx.tx_state.nIndex == wtxIn.tx_state.nIndex);
assert(wtx.tx_state.hashBlock = wtxIn.tx_state.hashBlock);
}My concern is that the new behavior might be less robust than previous behavior if there is a bug in higher level code that marks a transaction as confirmed in two different blocks without marking the transaction as unconfirmed in between.
There was a problem hiding this comment.
(re: #16624 (comment))
I don't like these new asserts. I think they could be triggered under the following scenario:
- a wallet tx is confirmed in a block
- the wallet is shut down
- the block chain re-orgs and the tx is included in a different block
- the wallet file is loaded on a sync'ed node
All this code is trying to do is update an existing wallet tx's Confirmation status with the new Confirmation status. I think we can just change it to:
if (wtxIn.tx_state.state != wtx.tx_state.state || wtxIn.tx_state.hashBlock != wtx.tx_state.hashBlock || wtxIn.tx_state.nIndex != wtx.tx_state.nIndex) {
There was a problem hiding this comment.
If these new asserts get triggered it would mean we have bugs in higher level code, but yes aborting maybe too strong given that this kind of bugs could be due to some likely API misuse in RPCs ? What's about logging them with a warning ?
I'm not in favor of proposed alternative as it may cause hidden inconsistencies if these aforementioned high level bugs are present
There was a problem hiding this comment.
I don't feel strongly, but I think asserts are way to go because we should not allow bugs and uncertainty in wallet event processing code. It's hard for me to imagine a scenario where it would be a win to use defensive programming in a place like this.
There was a problem hiding this comment.
@ryanofsky - do you agree that the scenario I describe above would trigger the assert?
There was a problem hiding this comment.
Oh, I didn't realize #16624 (comment) was describing a specific scenario, not just a list of possible events. I could see that scenerio triggering these asserts, and making the change you suggested an improvement and bugfix. It is likely some change is needed here, and the suggested one seems ok.
More ideally, though, it'd be really nice to have a functional test that triggers these asserts, and instead of hiding the problem of an unconfirmed transaction being left in the CONFIRMED state after a reorg, there would be code on startup that marks reorged transactions UNCONFIRMED before rescanning (could basically be the same code from #15931 that looks up block heights on startup).
Nice catch though (assuming this a bug)!
There was a problem hiding this comment.
I didn't realize #16624 (comment) was describing a specific scenario
sorry - changed my unordered list to an ordered list and updated wording to make this clearer.
There was a problem hiding this comment.
there would be code on startup that marks reorged transactions UNCONFIRMED before rescanning
Yes - I think if the wallet rescans from block at height x, it should first change all transactions that are CONFIRMED at height >x to UNCOFIRMED.
There was a problem hiding this comment.
I let asserts and move some bits from #15931 to transition status to UNCONFIRMED if blockHash not anymore in chain. Also added a wallet_reorgsrestore test with aforementioned scenario, without new code it triggers asserts
src/wallet/wallet.cpp
Outdated
There was a problem hiding this comment.
The variable name
tx_stateis the confusing part, because it's not a TxState
I think the current naming is ok, but if it I had to choose, I would probably do something like:
class CWalletTx
{
public:
enum Status { UNCONFIRMED, CONFIRMED, CONFLICTED, ABANDONED };
struct Confirmation {
Status status;
uint256 block_hash
int pos_in_block;
}
...
Confirmation m_confirm;
...
};
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26dc3c69e219a2db8d52980074951aff55a3)
It seems more safe to reset the whole struct instead of just one member. Maybe tx_state = TxConfirmation{};?
I think there may actually be a (theoretical) bug without this. If you unserialized an unconfirmed transaction into a CWalletTx that was not unconfirmed, deserialization code would not actually update the state to unconfirmed.
There was a problem hiding this comment.
Well strictly speaking, given that Init set to unconfirmed, it would be more wrongly updating to confirmed. But if hash is set IMO we have database corruption or bug in higher tracking logic. Updated serialization logic with this and beneath to make it more robust.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
In commit "Remove SyncTransaction for conflicted txn in CWallet::BlockConnected" (3e027c776556c3070a9c3460045a6e397b21ceb8)
Can we drop these tx_state.nIndex = 0; assignments or set them in all cases consistently? (There is a missing assignment in the unconfirmed case). I'd have a slight preference for just dropping these and the Init() call above reset everything to default, instead of repeating same default values multiple places.
ryanofsky
left a comment
There was a problem hiding this comment.
utACK f9f492638a488149acceb7b6487553862e919028. Only change since last review is renaming tx_state to tx_conf, which is reasonable. I left some comments with my previous review, but they aren't very important and are fine to ignore.
meshcollider
left a comment
There was a problem hiding this comment.
Looks good to me, utACK f9f492638a488149acceb7b6487553862e919028
@ryanofsky My concern is that the new behavior might be less robust than previous behavior if there is a bug in higher level code that marks a transaction as confirmed in two different blocks without marking the transaction as unconfirmed in between.
I agree, I was looking at this specific change too. I think it should be fine but the additional check wouldn't hurt 👍
f9f4926 to
5ef9e95
Compare
|
Updated at 5ef9e95 with @ryanofsky comments. Main changes are a more robust serialization logic and assert for already present txn in AddToWallet. This last one flagged a misbehavior of AddToWallet in ComputeTimeSmart (see commit message), so was worth adding. |
…tions f963a68 test: Add test for conflicted wallet tx notifications (Russell Yanofsky) Pull request description: Add test coverage for conflicted wallet transaction notifications so we can improve current behavior and avoid future regressions bitcoin#9240 - accidental break bitcoin#9479 - bug report bitcoin#9371 - fix bitcoin#16624 - accidental break bitcoin#18325 - bug report bitcoin#18600 - potential fix ACKs for top commit: laanwj: ACK f963a68 jonatack: re-ACK f963a68 MarcoFalke: ACK f963a68 Tree-SHA512: d3a7952a2d3dc2ff0800ef857575ea4ef9759c0917d58a7fc91e2db0ca3cc3baf0dd0cf9af61683f691e5fefb11afe8120bb5810c7037ed9ecedee385dd4aa07
Add test coverage for conflicted wallet transaction notifications so we improve current behavior and avoid future regressions bitcoin#9240 - accidental break bitcoin#9479 - bug report bitcoin#9371 - fix bitcoin#16624 - accidental break bitcoin#18325 - bug report bitcoin#18600 - potential fix Github-Pull: bitcoin#18878 Rebased-From: f963a68
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored by: Antoine Riard <[email protected]>
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored-by: Antoine Riard <[email protected]>
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored-by: Antoine Riard <[email protected]>
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored-by: Antoine Riard <[email protected]>
…otifications 7eaf86d trivial: Suggested cleanups to surrounding code (Russell Yanofsky) b604c5c wallet: Minimal fix to restore conflicted transaction notifications (Russell Yanofsky) Pull request description: This fix is a based on the fix by Antoine Riard (ariard) in #18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from #16624, causing issue #18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes #18325 Co-authored-by: Antoine Riard (ariard) ACKs for top commit: jonatack: Re-ACK 7eaf86d ariard: ACK 7eaf86d, reviewed, built and ran tests. MarcoFalke: ACK 7eaf86d 🍡 Tree-SHA512: 9a1efe975969bb522a9dd73c41064a9348887cb67883cd92c6571fd2df4321b9f4568363891abdaae14a3b9b168ef8142e95c373fc04677e46289b251fb84689
…ction notifications 7eaf86d trivial: Suggested cleanups to surrounding code (Russell Yanofsky) b604c5c wallet: Minimal fix to restore conflicted transaction notifications (Russell Yanofsky) Pull request description: This fix is a based on the fix by Antoine Riard (ariard) in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored-by: Antoine Riard (ariard) ACKs for top commit: jonatack: Re-ACK 7eaf86d ariard: ACK 7eaf86d, reviewed, built and ran tests. MarcoFalke: ACK 7eaf86d 🍡 Tree-SHA512: 9a1efe975969bb522a9dd73c41064a9348887cb67883cd92c6571fd2df4321b9f4568363891abdaae14a3b9b168ef8142e95c373fc04677e46289b251fb84689
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored-by: Antoine Riard <[email protected]> Github-Pull: bitcoin#18982 Rebased-From: b604c5c
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored-by: Antoine Riard <[email protected]> Github-Pull: bitcoin#18982 Rebased-From: b604c5c
Summary: Instead of relying on combination of hashBlock and nIndex values to manage tx in its lifecycle, we introduce 4 status : CONFIRMED, UNCONFIRMED, CONFLICTED, ABANDONED. hashBlock and nIndex magic values should only be used at serialization/deserialization for backward-compatibility. At block disconnection, we know flag txn as UNCONFIRMED where previously they kept their states until being override by a block connection or abandontransaction call. This is a change in behavior for which user may have to call abandon twice if transaction is disconnected and not accepted back in the mempool. We assert status transitioning right in AddToWallet. Doing so flagged a misbehavior in ComputeTimeSmart unit test where same tx is confirmed twice in different block. To avoid inconsistencies we unconfirmed tx before new connection in different block. We also remove a cs_main lock in test, as AddToWallet and its callees don't rely on locked chain. bitcoin/bitcoin@a31be09 --- Partial backport of Core [[bitcoin/bitcoin#16624 | PR16624]] Test Plan: ninja check check-functional Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D7048
…llet::BlockConnected Summary: We shouldn't rely on this sync call to get an accurate view of txn state, if a tx conflicts with one in mapTx we are going to update our wallet dependencies in AddToWalletIfInvolvingMe while conflicting txn get connected. If it doesn't conflict with one of our dependencies we are not going to track it anyway. This is a cleanup, as this SyncTransaction is redundant with the following one for confirmation which is triggering the MarkConflicted logic. We keep the loop because set of conflicted txn isn't same as txn included in block. bitcoin/bitcoin@7e89994 --- Depends on D7048 Partial backport of Core [[bitcoin/bitcoin#16624 | PR16624]] Test Plan: ninja check check-functional Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D7049
Summary: Add a LockChain method to CWallet to know if we can lock or query chain state safely. At tx loading, we rely on chain to know if hashBlock of tx is still in main chain. If not, we set its status to unconfirmed and reset its hashBlock/nIndex. If wallet loaded is the wallet-tool one, all wallet txn will show up with a height of zero. It doesn't matter as status is not used by wallet-tool. We take lock prematurely in CWallet::LoadWallet and CWallet::Verify to ensure that lock order is respected between cs_main an cs_wallet. bitcoin/bitcoin@40ede99 --- Depends on D7049 Partial backport of Core [[bitcoin/bitcoin#16624 | PR16624]] Test Plan: ninja check check-functional Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D7050
Summary: Test we change tx status at loading in case of reorgs while wallet was shutdown. bitcoin/bitcoin@442a87c Depends D7050 Concludes backport of Core [[bitcoin/bitcoin#16624 | PR16624]] Test Plan: ninja check check-functional Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D7051
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin/bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin/bitcoin#16624, causing issue bitcoin/bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes #18325 Co-authored-by: Antoine Riard <[email protected]> Github-Pull: #18982 Rebased-From: b604c5c
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09 and 7e89994 from bitcoin#16624, causing issue bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes bitcoin#18325 Co-authored-by: Antoine Riard <[email protected]> Github-Pull: bitcoin#18982 Rebased-From: b604c5c
At wallet loading, we rely on chain state querying to retrieve height of txn, to do so we ensure that lock order is respected between cs_main and cs_wallet. If wallet loaded is the wallet-tool one, all wallet txn will show up with a height of zero. It doesn't matter as confirmation height is not used by wallet-tool. Reorder arguments and document Confirmation calls to avoid ambiguity. Fixes nits left from bitcoin#16624
Add test coverage for conflicted wallet transaction notifications so we improve current behavior and avoid future regressions bitcoin/bitcoin#9240 - accidental break bitcoin/bitcoin#9479 - bug report bitcoin/bitcoin#9371 - fix bitcoin/bitcoin#16624 - accidental break bitcoin/bitcoin#18325 - bug report bitcoin/bitcoin#18600 - potential fix
This fix is a based on the fix by Antoine Riard <[email protected]> in bitcoin/bitcoin#18600. Unlike that PR, which implements some new behavior, this just restores previous wallet notification and status behavior for transactions removed from the mempool because they conflict with transactions in a block. The behavior was accidentally changed in two `CWallet::BlockConnected` updates: a31be09bfd77eed497a8e251d31358e16e2f2eb1 and 7e89994133725125dddbfa8d45484e3b9ed51c6e from bitcoin/bitcoin#16624, causing issue bitcoin/bitcoin#18325. The change here could be improved and replaced with a more comprehensive cleanup, so it includes a detailed comment explaining future considerations. Fixes #18325 Co-authored-by: Antoine Riard <[email protected]>
ryanofsky
left a comment
There was a problem hiding this comment.
Note: After this PR was merged, some unexpected behavior changes were found. One bug caused, another one partially fixed. See 16624 in the history section this wiki page https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking#history for details
src/wallet/wallet.cpp
Outdated
|
|
||
| for (const CTransactionRef& ptx : vtxConflicted) { | ||
| SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */); | ||
| SyncTransaction(ptx, CWalletTx::Status::CONFLICTED, {} /* block hash */, 0 /* position in block */); |
There was a problem hiding this comment.
In commit "Encapsulate tx status in a Confirmation struct" (a31be09)
This line is removed in the immediate next commit (7e89994), but technically it has some bugs. Original intent of this code was only ever to send -walletnotifynotifications, not to update transaction state, and the change should have used UNCONFIRMED instead of CONFLICTED to stay equivalent to previous code. Problems with the change:
- It was an undocumented change in behavior in what was supposed to be a refactoring commit
- It forgot to pass the block hash to
SyncTransactionso wasn't properly setting a conflicted state. Instead it set an inconsistent state that would sometimes be treated as unconfirmed, sometimes conflicted. - It would compound reorg problems wallet already has treating transactions that are no longer conflicted as conflicted, see bug-stale-conflicted.
In the future it would actually be possible to mark these transactions as conflicted instead of unconfirmed. Way this could be done is described idea-more-conflicts.
| // At block disconnection, this will change an abandoned transaction to | ||
| // be unconfirmed, whether or not the transaction is added back to the mempool. | ||
| // User may have to call abandontransaction again. It may be addressed in the | ||
| // future with a stickier abandoned state or even removing abandontransaction call. |
There was a problem hiding this comment.
In commit "Encapsulate tx status in a Confirmation struct" (a31be09)
Comment seems slightly off. I think the comment is right that abandontransaction would need to be called again in this case, but I don't think it would just be because of this code. The abandoned state would already have been lost previously when the block was connected.
src/wallet/wallet.cpp
Outdated
| // the notification that the conflicted transaction was evicted. | ||
|
|
||
| for (const CTransactionRef& ptx : vtxConflicted) { | ||
| SyncTransaction(ptx, CWalletTx::Status::CONFLICTED, {} /* block hash */, 0 /* position in block */); |
There was a problem hiding this comment.
In commit "Remove SyncTransaction for conflicted txn in CWallet::BlockConnected" (7e89994)
Dropping this SyncTransaction call was a bug, since it caused -walletnotify notifications to be lost. These were restored later in #18982, basically just reverting this commit, but with some updates due to notification code changes in the meantime, and using CONFIRMED instead of CONFLICTED to avoid a bug from the previous commit a31be09
While working on #15931, I've tried to rationalize tx state management to ease integration of block height tracking per-wallet tx. We currently rely on a combination of
hashBlockandnIndexwith magic value to determine tx confirmation, conflicted or abandoned state. It's hard to reason and error-prone. To solve that, we encapsulate these fields in aTxConfirmationstruct and introduce aTxStatemember that we update accordingly at block connection/disconnection.Following jnewbery recommendation, I've taken these changes in its own commit, and open a PR to get them first. It would ease review of aforementioned PR, but above all should ease fixing of long-term issues like :
MarkConflictedinLoadToWalletis likely useless if we track conflicts rights at block connectionMain changes of this PR to get right are tx update in
AddToWalletand serialization/deserialization logic.