Skip to content

Conversation

@maflcko
Copy link
Member

@maflcko maflcko commented Jun 28, 2021

A wallet method (like bumping the fee) might have set fInMempool to false because the transaction was removed from the mempool (See commit fa4e088).

Avoid setting it back to true (incorrectly) in the validation interface background thread.

Fixes #22357

@DrahtBot
Copy link
Contributor

DrahtBot commented Jun 28, 2021

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

No conflicts as of last run.

Copy link
Contributor

@promag promag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this, can't the transaction be removed from the mempool after isInMempool?

Wouldn't be more correct to SyncWithValidationInterfaceQueue at the end of the RPC call - in the same way it's called at the beginning? This ensures transactionRemovedFromMempool is processed to the caller?

@maflcko
Copy link
Member Author

maflcko commented Jun 29, 2021

@promag I agree. This is also my preferred solution (see #18840). Happy to reopen that one.

@ryanofsky
Copy link
Contributor

I could be missing something but it seems to me neither solution is clearly correct in all cases. The advantages of this solution over #18440 are that:

  • It's more obvious what this solution is trying to do. This solution is plainly updating mempool status to reflect what's in the mempool, where #18440 is broadening a sync that happens in a different part of the code without a specific explanation about why it's necessary.
  • #18440 waits for extra notifications to be handled that may not be relevant, blocking longer than it needs to.
  • It's not clear whether #18440 fixes the problem in all cases, since it's assuming that the mempool implementation will alway queue the removedFromMempool notification before the wallet RPC tries to sync with the queue. And even if this does work now, there is no unit test or other coverage for it to make sure the sync runs at the right point in the broken case, to make sure this stays fixed in the future.

But I do agree with the general point that a fully correct solution is probably a blocking solution. I just think the blocking should be targeted. Not more narrow or more wide than it needs to be. For example could add std::condition_variable m_mempool_cv; to CWallet and m_mempool_cv.notify_all() in transactionRemovedFromMempool() and m_mempool_cv.wait(lock, []{return !replaced_tx.fInMempool}) in bumpfee

@ryanofsky
Copy link
Contributor

For example could add std::condition_variable m_mempool_cv; to CWallet and m_mempool_cv.notify_all() in transactionRemovedFromMempool() and m_mempool_cv.wait(lock, []{return !replaced_tx.fInMempool}) in bumpfee

Thinking about this, in a really pathological case where a replaced transaction got added back to the mempool, this could hang. Dropping CWalletTx::fInMempool and giving transactions clear states like 730df28 from #21206 would help here, because the replaced transaction could ambiguously transition from the in-mempool state to an intermediate in-mempool-but-replaced state to the inactive state, and the cv.wait() could just wait for the transaction to be out of the intermediate state.

Any case, will ACK this PR. IMO, this is the best fix for we have for now, and it is straightforward and simple, even if it is not 100% right in every case and doesn't have a fancy test.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK fa9ef0e8c6f379499ba7415040b76a76afdb02dc. This is a pretty simple fix that should make tests more reliable and the RPC more reliable in applications and should be better than status quo even if it is not 100% in every case (see previous discussion).

@luke-jr
Copy link
Member

luke-jr commented Jun 29, 2021

Should we do this in transactionRemovedFromMempool too perhaps?

luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Jun 29, 2021
… not in the mempool

Github-Pull: bitcoin#22359
Rebased-From: fa9ef0e8c6f379499ba7415040b76a76afdb02dc
@promag
Copy link
Contributor

promag commented Jun 29, 2021

Maybe it should avoid fInMempool in some cases, like in CWallet::AbandonTransaction:

@@ -1078,10 +1078,11 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
     std::set<uint256> done;

     // Can't mark abandoned if confirmed or in mempool
+    if (chain().isInMempool(hashTx)) return false;
     auto it = mapWallet.find(hashTx);
     assert(it != mapWallet.end());
     const CWalletTx& origtx = it->second;
-    if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) {
+    if (origtx.GetDepthInMainChain() != 0) {
         return false;
     }

Copy link
Contributor

@promag promag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK fa9ef0e8c6f379499ba7415040b76a76afdb02dc.

@maflcko
Copy link
Member Author

maflcko commented Jul 1, 2021

Should we do this in transactionRemovedFromMempool too perhaps?

Thx, done.

@maflcko
Copy link
Member Author

maflcko commented Jul 1, 2021

Maybe it should avoid fInMempool in some cases, like in CWallet::AbandonTransaction:

Maybe the mempool status should be removed and simply be queried when needed? Otherwise it just seems like applying random patches that happen to improve the situation, but don't actually fix the underlying issue.

@ryanofsky
Copy link
Contributor

ryanofsky commented Jul 1, 2021

Maybe the mempool status should be removed and simply be queried when needed?

I think there are two drawbacks to querying the mempool when needed:

  • Conceptually it seems less appealing because it means coin selection, balance checking, etc code is combining mempool and chain state from two different points in time instead of one point. Instead of using chain and mempool state from the time of last notification, it is combining data from the last notification time with data from the current time. I don't think the consequences of this are very bad but they could make coin selection and balance checking glitchy or weird if there is a backlog of notifications.

  • At least according to 17220d6 which introduced the cached fInMempool field, there are some reasons to think this may be worse for performance.

Otherwise it just seems like applying random patches that happen to improve the situation, but don't actually fix the underlying issue.

I think if we combine transaction states (730df28 from #21206) with the notify suggestion mentioned earlier (in #22359 (comment) and #22359 (comment)), that is a clean and robust solution that gets rid of all race conditions. But since that requires reviewing and writing more code, using tx.fInMempool = chain.isInMempool(tx.GetHash()); is at least a straighforward workaround in the meantime.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK fa6fd3d. Only change since last review is extending workaround to transactionRemovedFromMempool. Since we know this workaround is imperfect and the goal of this PR is mainly to fix CI errors, I would probably be inclined to limit the workaround to as few places as possible where we have seen actual failures, instead of adding the workaround to as many places as possible, where there is some chance it might trigger new failures. But since this workaround is so straightforward and almost looks like a real fix, probably it doesn't matter.

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Jul 1, 2021
Block waiting for transactions to enter the mempool to avoid race
conditions as discussed
bitcoin#22359 (comment)
@ryanofsky
Copy link
Contributor

ryanofsky commented Jul 1, 2021

I think if we combine transaction states (730df28 from #21206) with the notify suggestion mentioned earlier (in #22359 (comment) and #22359 (comment)), that is a clean and robust solution that gets rid of all race conditions.

FWIW, I started implementing this in f54631d (branch). It could be cleaned up more, and doesn't have a dedicated test, and might not be totally complete (might need one or two more cv.wait() calls), but this shows the general idea.


EDIT: Updated d622b28 -> f54631d (compare)

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Jul 2, 2021
Block waiting for transactions to enter the mempool to avoid race
conditions as discussed
bitcoin#22359 (comment)
Copy link
Contributor

@meshcollider meshcollider left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK fa6fd3d

@meshcollider meshcollider merged commit a162edf into bitcoin:master Aug 9, 2021
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Aug 10, 2021
…dToMempool when tx is not in the mempool

fa6fd3d wallet: Properly set fInMempool in mempool notifications (MarcoFalke)

Pull request description:

  A wallet method (like bumping the fee) might have set `fInMempool` to false because the transaction was removed from the mempool (See commit fa4e088).

  Avoid setting it back to true (incorrectly) in the validation interface background thread.

  Fixes bitcoin#22357

ACKs for top commit:
  ryanofsky:
    Code review ACK fa6fd3d. Only change since last review is extending workaround to `transactionRemovedFromMempool`. Since we know this workaround is imperfect and the goal of this PR is mainly to fix CI errors, I would probably be inclined to limit the workaround to as few places as possible where we have seen actual failures, instead of adding the workaround to as many places as possible, where there is some chance it might trigger new failures. But since this workaround is so straightforward and almost looks like a real fix, probably it doesn't matter.
  meshcollider:
    utACK fa6fd3d

Tree-SHA512: d690136a577f1f532aa1fee80d3f6600ff7fc61286fbf564a53d7938d5ae52d33f0dbb0fef8b8c041a4970fb424f0b9f1ee7ce791e0ff8354e0000ecc9e22b84
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Oct 10, 2021
luke-jr added a commit to bitcoinknots/bitcoin that referenced this pull request Dec 14, 2021
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 16, 2022
@maflcko maflcko deleted the 2106-walletFix branch August 22, 2022 11:28
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: failure in feature_backwards_compatibility --descriptors "Transaction not eligible for abandonment"

7 participants