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
refactor: Make CWalletTx sync state type-safe #21206
Conversation
This commit just moves code without making any changes. It can be reviewed with `git log -p -n1 --color-moved=dimmed_zebra` Motivation for this change is to make wallet.cpp/h less monolithic and start to make wallet transaction state tracking comprehensible so bugs in https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking can be fixed safely without introducing new problems. This commit moves wallet classes and methods that deal with transactions out of wallet.cpp/.h into better organized files: - transaction.cpp/.h - CWalletTx and CMerkleTX class definitions - receive.cpp/.h - functions checking received transactions and computing balances - spend.cpp/.h - functions creating transactions and finding spendable coins After bitcoin#20773, when loading is separated from syncing it will also be possible to move more wallet.cpp/.h functions to: - sync.cpp/.h - functions handling chain notifications and rescanning This commit arranges receive.cpp and spend.cpp functions in dependency order so it's possible to skim receive.cpp and get an idea of how computing balances works, and skim spend.cpp and get an idea of how transactions are created, without having to jump all over wallet.cpp where functions are not in order and there is a lot of unrelated uncode. Followup commit "refactor: Detach wallet transaction methods" in bitcoin#21206 follows up this PR and tweaks function names and arguments to reflect new locations. The two commits are split into separate PRs because this commit is more work to maintain and less work to review, while the other commit is less work to maintain and more work to review, so hopefully this commit can be merged earlier.
eb7814d
to
792d72e
Compare
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. |
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.
Approach ACK (also base PR).
This makes it a lot easier to reason about transaction state. Another great achievement is dropping wallet dependency from transaction. 👏
This commit just moves code without making any changes. It can be reviewed with `git log -p -n1 --color-moved=dimmed_zebra` Motivation for this change is to make wallet.cpp/h less monolithic and start to make wallet transaction state tracking comprehensible so bugs in https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking can be fixed safely without introducing new problems. This commit moves wallet classes and methods that deal with transactions out of wallet.cpp/.h into better organized files: - transaction.cpp/.h - CWalletTx and CMerkleTX class definitions - receive.cpp/.h - functions checking received transactions and computing balances - spend.cpp/.h - functions creating transactions and finding spendable coins After bitcoin#20773, when loading is separated from syncing it will also be possible to move more wallet.cpp/.h functions to: - sync.cpp/.h - functions handling chain notifications and rescanning This commit arranges receive.cpp and spend.cpp functions in dependency order so it's possible to skim receive.cpp and get an idea of how computing balances works, and skim spend.cpp and get an idea of how transactions are created, without having to jump all over wallet.cpp where functions are not in order and there is a lot of unrelated uncode. Followup commit "refactor: Detach wallet transaction methods" in bitcoin#21206 follows up this PR and tweaks function names and arguments to reflect new locations. The two commits are split into separate PRs because this commit is more work to maintain and less work to review, while the other commit is less work to maintain and more work to review, so hopefully this commit can be merged earlier.
This commit just moves functions without making any changes. It can be reviewed with `git log -p -n1 --color-moved=dimmed_zebra` Motivation for this change is to make wallet.cpp/h less monolithic and start to make wallet transaction state tracking comprehensible so bugs in https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking can be fixed safely without introducing new problems. This commit moves wallet classes and methods that deal with transactions out of wallet.cpp/.h into better organized files: - transaction.cpp/.h - CWalletTx and CMerkleTx class definitions - receive.cpp/.h - functions checking received transactions and computing balances - spend.cpp/.h - functions creating transactions and finding spendable coins After bitcoin#20773, when loading is separated from syncing it will also be possible to move more wallet.cpp/.h functions to: - sync.cpp/.h - functions handling chain notifications and rescanning This commit arranges receive.cpp and spend.cpp functions in dependency order so it's possible to skim receive.cpp and get an idea of how computing balances works, and skim spend.cpp and get an idea of how transactions are created, without having to jump all over wallet.cpp where functions are not in order and there is a lot of unrelated code. Followup commit "refactor: Detach wallet transaction methods" in bitcoin#21206 follows up this PR and tweaks function names and arguments to reflect new locations. The two commits are split into separate PRs because this commit is more work to maintain and less work to review, while the other commit is less work to maintain and more work to review, so hopefully this commit can be merged earlier.
Current CWalletTx state representation makes it possible to set inconsistent states that won't be handled correctly by wallet sync code or serialized & deserialized back into the same form. For example, it is possible to call setConflicted without setting a conflicting block hash, or setConfirmed with no transaction index. And it's possible update individual m_confirm and fInMempool data fields without setting an overall consistent state that can be serialized and handled correctly. Fix this without changing behavior by using std::variant, instead of an enum and collection of fields, to represent sync state, so state tracking code is safer and more legible. This is a first step to fixing state tracking bugs https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking, by adding an extra margin of safety that can prevent new bugs from being introduced as existing bugs are fixed.
3c6fdae
to
78c8ac8
Compare
Rebased 3c6fdae -> 78c8ac8 ( |
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-utACK 78c8ac8
Concept ACK, wil review today. |
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 78c8ac8.
In a follow-up, we can consolidate updates to m_state
, for instance, the 2nd update can be avoided:
transactionAddedToMempool
SyncTransaction
AddToWalletIfInvolvingMe
AddToWallet
m_state = ...
RefreshMempoolStatus
m_state = ...
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.
Approach ACK / light code review ACK 78c8ac8
78c8ac8
to
d8ee8f3
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.
Thanks for reviews and suggestions!
Updated 78c8ac8 -> d8ee8f3 (pr/txstate.25
-> pr/txstate.26
, compare)
// is needed before confirm again with different block. | ||
return wallet.AddToWallet(MakeTransactionRef(tx), confirm, [&](CWalletTx& wtx, bool /* new_tx */) { | ||
wtx.setUnconfirmed(); | ||
return wallet.AddToWallet(MakeTransactionRef(tx), state, [&](CWalletTx& wtx, bool /* new_tx */) { |
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.
(if need to retouch)
return wallet.AddToWallet(MakeTransactionRef(tx), state, [&](CWalletTx& wtx, bool /* new_tx */) { | |
return wallet.AddToWallet(MakeTransactionRef(tx), state, [&](CWalletTx& wtx, /*new_tx=*/bool) { |
// serialized transaction. | ||
wtx.m_confirm.block_height = height; | ||
} else if (wtx.isConflicted() || wtx.isConfirmed()) { | ||
auto lookup_block = [&](const uint256& hash, int& height, TxState& state) { | ||
// If tx block (or conflicting block) was reorged out of chain | ||
// while the wallet was shutdown, change tx status to UNCONFIRMED | ||
// and reset block height, hash, and index. ABANDONED tx don't have |
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 documentation be updated here as well?
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 d8ee8f3
//! https://en.cppreference.com/w/cpp/utility/variant/visit#Example | ||
template<class... Ts> struct Overloaded : Ts... { using Ts::operator()...; }; | ||
|
||
//! Explicit deduction guide (not needed as of C++20) |
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.
As for Clang, this explicit deduction guide can be dropped for versions >=17 only, unfortunately.
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.
This is part of #29042.
Current
CWalletTx
state representation makes it possible to set inconsistent states that won't be handled correctly by wallet sync code or serialized & deserialized back into the same form.For example, it is possible to call
setConflicted
without setting a conflicting block hash, orsetConfirmed
with no transaction index. And it's possible update individualm_confirm
andfInMempool
data fields without setting an overall consistent state that can be serialized and handled correctly.Fix this without changing behavior by using
std::variant
, instead of an enum and collection of fields, to represent sync state, so state tracking code is safer and more legible.This is a first step to fixing state tracking bugs https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking, by adding an extra margin of safety that can prevent new bugs from being introduced as existing bugs are fixed.