Fetch the parent tx when watching a delayed transaction - #878
Merged
Merged
Conversation
pm47
added this pull request to stack #881
September 17, 2026 14:38
pm47
removed this pull request from stack #881
September 18, 2026 09:26
When publishing a tx that has a relative delay, we watch the output it spends until the delay elapses, which requires the script of that output to compute an electrum script hash. We derived that script from the witness, which only works for p2wpkh and p2wsh: for taproot inputs we hashed the control block into a p2wsh script and watched a script hash that doesn't exist anywhere. We now fetch the parent tx and read the script directly, which is also the only option for a taproot key path spend, where the witness is a signature that doesn't reveal the output key. This was never triggered because the broken watch never had to work: we always set another watch on that same tx (e.g. `ClosingTxConfirmed` on the commit tx) whose script is correct, and we trigger watch-confirmed by txid whenever we process the history of *any* script hash. The correct watch was thus doing the work of the broken one, both in production and in `ElectrumWatcherIntegrationTest`. As a side-effect, we no longer throw on a tx that spends a non-segwit input, whose witness is empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…880) When publishing a delayed transaction, we now fetch its parent to get the script of the output it spends. If that fetch fails we put the transaction back into the publish queue, but that queue was only drained on reconnection: `getTx` returns null after two 10s timeouts or on any electrum error, without losing the connection, so on a long-lived connection the transaction was dropped and no `ParentTxConfirmed` watch was ever registered. We now also drain the publish queue whenever we receive a new block, which bounds the retry delay to one block instead of one reconnection. The publish handler is extracted into `processPublish`, which lets the queue be drained by calling it directly instead of re-sending to our own mailbox: that self-send could deadlock the watcher if the queue exceeded the mailbox buffer. The queue is cleared before being replayed, since `processPublish` puts failed transactions back into it.
pm47
force-pushed
the
fetch-parent-delayed
branch
from
September 18, 2026 11:55
9235a47 to
1c1e603
Compare
sstone
approved these changes
Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When publishing a tx that has a relative delay, we watch the output it spends until the delay elapses, which requires the script of that output to compute an electrum script hash. We derived that script from the witness, which only works for p2wpkh and p2wsh: for taproot inputs we hashed the control block into a p2wsh script and watched a script hash that doesn't exist anywhere. We now fetch the parent tx and read the script directly, which is also the only option for a taproot key path spend, where the witness is a signature that doesn't reveal the output key.
This was never triggered because the broken watch never had to work: we always set another watch on that same tx (e.g.
ClosingTxConfirmedon the commit tx) whose script is correct, and we trigger watch-confirmed by txid whenever we process the history of any script hash. The correct watch was thus doing the work of the broken one, both in production and inElectrumWatcherIntegrationTest.As a side-effect, we no longer throw on a tx that spends a non-segwit input, whose witness is empty.