-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet: encapsulate transactions state #16624
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
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 |
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. I added tests for backwards compatibility of abandoned and conflicted wallet transactions to #12134. Those still pass if I apply this PR, which gives some assurance we didn't break the serialization.
src/wallet/wallet.h
Outdated
@@ -396,7 +396,7 @@ class CWalletTx | |||
private: | |||
const CWallet* pwallet; | |||
|
|||
/** Constant used in hashBlock to indicate tx has been abandoned */ | |||
/** Constant used in hashBlock to indicate tx has been abandoned, */ |
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.
Nit: why the trailing comma?
src/wallet/wallet.h
Outdated
@@ -477,16 +475,46 @@ class CWalletTx | |||
fInMempool = false; | |||
nChangeCached = 0; | |||
nOrderPos = -1; | |||
tx_state.hashBlock = uint256(); | |||
tx_state.nIndex = 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.
Why is the default initialization changed to 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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes see comment on top of TxConfirmation
, should be improved ?
src/wallet/wallet.h
Outdated
* Older clients interpret nIndex == -1 as unconfirmed for backward | ||
* compatibility (pre-commit 9ac63d6). | ||
*/ | ||
int nIndex; |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this backwards compatibility feature entirely to the serialization code, and then make
nIndex
auint
(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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See below, it depends on if nIndex has any meaning.
src/wallet/wallet.h
Outdated
uint256 hashBlock; | ||
/* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to | ||
* the earliest block in the chain we know this or any in-wallet dependency conflicts | ||
* with, but if hashBlock is null it means transaction is abandoned. An nIndex >= 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.
Mention that nIndex
is a block height?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
void setConflicted() { tx_state.state = CWalletTx::CONFLICTED; } | ||
bool isUnconfirmed() const { return tx_state.state == CWalletTx::UNCONFIRMED; } | ||
void setUnconfirmed() { tx_state.state = CWalletTx::UNCONFIRMED; } | ||
bool isConfirmed() const { return tx_state.state == CWalletTx::CONFIRMED; } |
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.
isConfirmed()
does not seem to be tested as the others. Add a test? :-)
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.
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
/* New transactions start in the UNCONFIRMED. At BlockConnected, | ||
* they will transition to CONFIRMED. In case of reorg, at BlockDisconnected, | ||
* they rollback to UNCONFIRMED. If we detect a conflicting transaction at | ||
* block connection, we update conflited tx and its dependencies as CONFLICTED. |
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 be "conflicted"? :-)
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.
Some questions, but this looks very good and is a welcome change that should make code more readable.
src/wallet/wallet.h
Outdated
uint256 hashBlock; | ||
/* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to | ||
* the earliest block in the chain we know this or any in-wallet dependency conflicts | ||
* with, but if hashBlock is null it means transaction is abandoned. An nIndex >= 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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -1249,8 +1238,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256 | |||
CWalletTx wtx(this, ptx); | |||
|
|||
// Get merkle branch if transaction was found in a block | |||
if (!block_hash.IsNull()) | |||
wtx.SetMerkleBranch(block_hash, posInBlock); | |||
wtx.SetMerkleBranch(state, block_hash, posInBlock); |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can live with changing it now, but we should drop the comment then.
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.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26d)
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
* Older clients interpret nIndex == -1 as unconfirmed for backward | ||
* compatibility (pre-commit 9ac63d6). | ||
*/ | ||
int nIndex; |
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.
Can we move this backwards compatibility feature entirely to the serialization code, and then make
nIndex
auint
(for clarity)?
Personally -0 on this. Imo, unsigned types provide no safety and are terrible for everything except bitfields.
src/wallet/wallet.h
Outdated
* Older clients interpret nIndex == -1 as unconfirmed for backward | ||
* compatibility (pre-commit 9ac63d6). | ||
*/ | ||
int nIndex; |
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.
Just a suggestion, but I'd add = 0
here and = UNCONFIRMED
on the line below to simplify constructor and deserialization code.
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.
done
src/wallet/wallet.h
Outdated
@@ -477,16 +475,46 @@ class CWalletTx | |||
fInMempool = false; | |||
nChangeCached = 0; | |||
nOrderPos = -1; | |||
tx_state.hashBlock = uint256(); | |||
tx_state.nIndex = 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.
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
tx_state.nIndex = 0; | ||
setAbandoned(); | ||
} else if (serializedIndex == -1) { | ||
tx_state.nIndex = -1; |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right that an inconsistency from my part. Corrected.
src/wallet/wallet.cpp
Outdated
@@ -1415,7 +1415,6 @@ void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransaction | |||
// the notification that the conflicted transaction was evicted. | |||
|
|||
for (const CTransactionRef& ptx : vtxConflicted) { | |||
SyncTransaction(ptx, CWalletTx::TxState::CONFLICTED, {} /* block hash */, 0 /* position in block */); |
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.
In commit "Remove SyncTransaction for conflicted txn in CWallet::BlockConnected" (0e3212a)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
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 6cc34fc
src/wallet/wallet.cpp
Outdated
@@ -1415,7 +1415,6 @@ void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransaction | |||
// the notification that the conflicted transaction was evicted. | |||
|
|||
for (const CTransactionRef& ptx : vtxConflicted) { | |||
SyncTransaction(ptx, CWalletTx::TxState::CONFLICTED, {} /* block hash */, 0 /* position in block */); |
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.
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
int nIndex; | ||
struct TxConfirmation { | ||
uint256 hashBlock; | ||
/* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to the deserialization code!
src/wallet/wallet.cpp
Outdated
{ | ||
// Update the tx's hashBlock | ||
hashBlock = block_hash; | ||
tx_state.hashBlock = block_hash; |
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.
TxConfirmation tx_state
is now a confusingly named variable.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name tx_state
is the confusing part, because it's not a TxState
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.
The variable name
tx_state
is 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced by tx_conf
on f9f4926
src/wallet/wallet.cpp
Outdated
@@ -1249,8 +1238,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256 | |||
CWalletTx wtx(this, ptx); | |||
|
|||
// Get merkle branch if transaction was found in a block | |||
if (!block_hash.IsNull()) | |||
wtx.SetMerkleBranch(block_hash, posInBlock); | |||
wtx.SetMerkleBranch(state, block_hash, posInBlock); |
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.
I can live with changing it now, but we should drop the comment then.
src/wallet/wallet.cpp
Outdated
@@ -1383,8 +1372,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) | |||
} | |||
} | |||
|
|||
void CWallet::SyncTransaction(const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool update_tx) { | |||
if (!AddToWalletIfInvolvingMe(ptx, block_hash, posInBlock, update_tx)) | |||
void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::TxState state, const uint256& block_hash, int posInBlock, bool update_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.
posInBlock
could be a good rename candidate for nIndex
, though probably not worth making the diff bigger.
6cc34fc
to
3e027c7
Compare
de4459c
to
f9f4926
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.
utACK 3e027c7
src/wallet/wallet.cpp
Outdated
@@ -1249,8 +1238,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256 | |||
CWalletTx wtx(this, ptx); | |||
|
|||
// Get merkle branch if transaction was found in a block | |||
if (!block_hash.IsNull()) | |||
wtx.SetMerkleBranch(block_hash, posInBlock); | |||
wtx.SetMerkleBranch(state, block_hash, posInBlock); |
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.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26d)
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.
if (wtxIn.tx_state.state != wtx.tx_state.state) { | ||
wtx.tx_state.state = wtxIn.tx_state.state; | ||
wtx.tx_state.nIndex = wtxIn.tx_state.nIndex; | ||
wtx.tx_state.hashBlock = wtxIn.tx_state.hashBlock; | ||
fUpdated = true; | ||
} |
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.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26d)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
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: #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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ryanofsky - do you agree that the scenario I describe above would trigger the assert?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
{ | ||
// Update the tx's hashBlock | ||
hashBlock = block_hash; | ||
tx_state.hashBlock = block_hash; |
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.
The variable name
tx_state
is 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
@@ -477,16 +477,37 @@ class CWalletTx | |||
fInMempool = false; | |||
nChangeCached = 0; | |||
nOrderPos = -1; | |||
tx_state.hashBlock = uint256(); |
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.
In commit "Encapsulate tx state in a TxConfirmation struct" (8a7d26d)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
tx_state.hashBlock = uint256(); | ||
setAbandoned(); | ||
} else if (serializedIndex == -1) { | ||
tx_state.nIndex = 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.
In commit "Remove SyncTransaction for conflicted txn in CWallet::BlockConnected" (3e027c7)
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.
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 f9f4926. 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.
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.
Looks good to me, utACK f9f4926
@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. |
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
…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
…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
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
[bitcoin#14624](bitcoin/bitcoin#14624) bls refactoring to resolve clang warnings [bitcoin#16426](bitcoin/bitcoin#16426) - cs_wallet lock order refactoring and reduce cs_main locking atomic smartnode_connection (allow read/write by different threads simultaneously) cs_mnauth locks on verifiedProRegTxHash read RecursiveMutex at locking access to activeSmartNode [bitcoin#14307](bitcoin/bitcoin#14307) - Consolidate redundant implementation of ParseHashStr [bitcoin#13399](bitcoin/bitcoin#13399) - rpc: Add submitheader built-in miner deleted [bitcoin#17781](bitcoin/bitcoin#17781) - Remove mempool global from miner [bitcoin#16623](bitcoin/bitcoin#16624) - encapsulate transactions state [bitcoin#15931](bitcoin/bitcoin#15931) - Remove GetDepthInMainChain dependency on locked chain interface Pass CConnman to function against global pointer [bitcoin#16839](bitcoin/bitcoin#16839) - Replace Connman and BanMan globals with NodeContext local [bitcoin#16034](bitcoin/bitcoin#16034) - refactoring: Rename LockAnnotation to LockAssertion and add run-time check to it [bitcoin#17564](bitcoin/bitcoin#17564) - Use mempool from node context instead of global [bitcoin#18740](bitcoin/bitcoin#18740) - Remove g_rpc_node global [bitcoin#19096](bitcoin/bitcoin#19096) - Remove g_rpc_chain global [bitcoin#18338](bitcoin/bitcoin#18338) - Fix wallet unload race condition other fixes and tweaks
…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
…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
…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
…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
…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
…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
…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
…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
…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
…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
…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
…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
…notifications Backport notice: we don't have bumpfee feature, so, only some part of code is backported 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 fixup dashify of feature_notifications
…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 fixup dashify of feature_notifications
…notifications Backport notice: we don't have bumpfee feature, so, only some part of code is backported 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 fixup dashify of feature_notifications
…notifications Backport notice: we don't have bumpfee feature, so, only some part of code is backported 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 fixup dashify of feature_notifications
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
hashBlock
andnIndex
with 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 aTxConfirmation
struct and introduce aTxState
member 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 :
MarkConflicted
inLoadToWallet
is likely useless if we track conflicts rights at block connectionMain changes of this PR to get right are tx update in
AddToWallet
and serialization/deserialization logic.