Skip to content
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

poller: unspend expired before new spend #965

Merged

Conversation

jp1ac4
Copy link
Collaborator

@jp1ac4 jp1ac4 commented Feb 8, 2024

This change ensures that the spend txid of a coin is updated directly to another value in case a conflicting spend is detected.

The previous order caused the spend txid to first be cleared, which would misleadingly make the coin appear as confirmed
rather than spending.

I've added a new utils function for the functional tests that is a slight generalisation of wait_for with an additional condition that must always be met while waiting.

wait_for now calls this new function with the condition being one that is always true.

@jp1ac4 jp1ac4 force-pushed the poller-unspend-before-new-spending branch 2 times, most recently from 6a4967c to 8c7fec7 Compare February 9, 2024 08:49
@jp1ac4
Copy link
Collaborator Author

jp1ac4 commented Feb 9, 2024

Refactored the new utils function.

@jp1ac4 jp1ac4 mentioned this pull request Feb 9, 2024
@darosior darosior self-requested a review February 21, 2024 11:30
@jp1ac4 jp1ac4 force-pushed the poller-unspend-before-new-spending branch from 8c7fec7 to eb36a23 Compare March 8, 2024 08:14
@jp1ac4
Copy link
Collaborator Author

jp1ac4 commented Mar 8, 2024

Rebased on master.

Copy link
Member

@darosior darosior left a comment

Choose a reason for hiding this comment

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

The rationale for this ordering is to make sure a coin marked as spending in this round which then gets marked as expired does get recorded correctly.

This is because we check the spend status of both the coins marked spending in DB but also the newly spending coins:

// Mark coins in a spending state whose Spend transaction was confirmed as such. Note we
// need to take into account the freshly marked as spending coins as well, as their spend
// may have been confirmed within the previous tip and the current one, and we may not poll
// this chunk of the chain anymore.
let spending_coins: Vec<(bitcoin::OutPoint, bitcoin::Txid)> = db_conn
.list_spending_coins()
.values()
.map(|coin| (coin.outpoint, coin.spend_txid.expect("Coin is spending")))
.chain(spending.iter().cloned())
.collect();
let (spent, expired_spending) = bit.spent_coins(spending_coins.as_slice());

It seems this change could only cause issues in the unlikely scenario a coin gets detected as spending and "expires" right before we check its spend status in spent_coins and therefore we miss it did expire. But in this case it would just be marked as such at the next polling round, that's fine.

@@ -46,6 +56,10 @@ def wait_for(success, timeout=TIMEOUT, debug_fn=None):
interval = 5
if time.time() > start_time + timeout:
raise ValueError("Error waiting for {}", success)
if not condition():
Copy link
Member

Choose a reason for hiding this comment

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

I think it's a bit fragile: it's possible for a caller to provide a condition function such as it returns True above and False here during the second call. It would be more robust to change the loop above to be:

while True:
    if success() or time >= start_time + timeout:
        break
    if condition():
        raise

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's true. Perhaps we could also do:

while True:
    if time.time() >= start_time + timeout:
        raise
    if not condition():
        raise
    if success():
        return

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've now updated this function.

Copy link
Member

@darosior darosior left a comment

Choose a reason for hiding this comment

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

Concept ACK. It does make a lot of sense, i think that's a bugfix and is harmless. I'd like to sleep on it though.

@darosior
Copy link
Member

In a followup it would be worthwile to introduce this new wait_for_while_condition_holds helper for the rescan tests.

jp1ac4 added 2 commits March 11, 2024 15:41
This adds a new utils function that is a slight generalisation
of `wait_for` with an additional condition that must always be
met while waiting.

`wait_for` now calls this new function with the condition being
one that is always true.
This change ensures that the spend txid of a coin is updated
directly to another value in case a conflicting spend is
detected.

The previous order caused the spend txid to first be cleared,
which would misleadingly make the coin appear as confirmed
rather than spending.
@jp1ac4 jp1ac4 force-pushed the poller-unspend-before-new-spending branch from eb36a23 to cc1de1d Compare March 11, 2024 15:42
@jp1ac4
Copy link
Collaborator Author

jp1ac4 commented Mar 11, 2024

Updated wait_for_while_condition_holds helper function.

@darosior
Copy link
Member

ACK cc1de1d

@darosior darosior merged commit 8d33f49 into wizardsardine:master Mar 12, 2024
18 checks passed
@jp1ac4 jp1ac4 deleted the poller-unspend-before-new-spending branch March 18, 2024 16:55
edouardparis added a commit that referenced this pull request Apr 19, 2024
1c99376 gui(app): cache unconfirmed & confirmed coins only (jp1ac4)
daf9f85 gui(spend): filter for unconfirmed & confirmed coins (jp1ac4)
982220d gui(home): filter for unconfirmed & confirmed coins (jp1ac4)
85b053f gui(recovery): filter for unconfirmed & confirmed coins (jp1ac4)
c9fcfae gui(recovery): filter coins by psbt outpoints (jp1ac4)
4509bd6 gui(coins): filter for confirmed & unconfirmed (jp1ac4)
685f83b gui(transactions): remove unused list_coins (jp1ac4)
21f8704 gui(transactions): filter coins by tx outpoints (jp1ac4)
ddd1e84 gui(psbt): get conflicting txs from filtered coins (jp1ac4)
4641d9e gui(daemon): filter coins using spend txs prev outpoints (jp1ac4)
aa578ba gui(daemon): filter coins for pending txs (jp1ac4)
4612159 gui(daemon): filter outpoints from txs inputs & outputs (jp1ac4)
f3fdb96 gui(daemon): extract common function for historytxs (jp1ac4)
6c7ca2c gui(daemon): add optional filter to `list_coins` (jp1ac4)

Pull request description:

  This is to resolve #677.

  As well as adding the filters to the daemon interface, I've applied filters in separate commits to different sections of the GUI.

  This PR builds on changes from #958 and #965. The latter is required when filtering for pending transactions so that a coin whose spending txid changes (e.g. due to RBF) remains as spending.

ACKs for top commit:
  edouardparis:
    ACK 1c99376

Tree-SHA512: c8b9c68a8344df1dbb04b22e315234dec7ae0c18bfc697f88296d3a4b5e7276a48005db6caf1a0b89a23e174f72118ad10945118be8cd96fbee20ba79c12d721
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants