Skip to content

lnworker: keep watchtower sync alive on invalid get_ctn replies - #10857

Open
rdymac wants to merge 1 commit into
spesmilo:masterfrom
rdymac:watchtower_invalid_ctn
Open

lnworker: keep watchtower sync alive on invalid get_ctn replies#10857
rdymac wants to merge 1 commit into
spesmilo:masterfrom
rdymac:watchtower_invalid_ctn

Conversation

@rdymac

@rdymac rdymac commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Validate get_ctn from a remote watchtower (must be a non-negative int) before feeding it to range() / assert ctn >= 0.
  • Catch per-channel and unexpected errors inside the while True sync loop so one bad reply cannot kill watchtower updates for the rest of the session.

A JSON-RPC get_ctn result was used unvalidated. A negative or non-int value (including HTTP non-200 returning an 'Error: …' string) escaped the loop's except aiohttp.ClientError. The coroutine is spawned once in start_network, so syncing then stopped for all channels until restart.

Tests

Optional tests live on a separate branch, not in this PR: watchtower_invalid_ctn-test. Pull that if you want the unit tests as well.

Comments already received on the suggested fix

util.py:763 already has is_non_negative_integer, and lnworker.py:38 already imports from .util.

is_non_negative_integer(True)  -> True     # bool subclasses int

is_integer is just isinstance(val, int), so the helper accepts True/False. type(watchtower_ctn) is not int rejects them. A watchtower answering JSON true would otherwise become range(2, n) silently treated as ctn=1.

Suggested in-code comment:
python

# note: `type(x) is int` rather than isinstance/is_non_negative_integer:
#       bool subclasses int, and a JSON `true` would otherwise be read as ctn=1
if type(watchtower_ctn) is not int or watchtower_ctn < 0:

Co-authored-by: Cursor <cursoragent@cursor.com>
@rdymac
rdymac marked this pull request as ready for review August 15, 2026 16:16
@ecdsa

ecdsa commented Aug 20, 2026

Copy link
Copy Markdown
Member

Comments already received on the suggested fix

Not sure who already sent you comments and where. Was that on Github? Or is it just your AI referring to comments you gave to it?

Catch per-channel and unexpected errors inside the while True sync loop so one bad reply cannot kill watchtower updates for the rest of the session.

This is overkill. If the watchtower is deliberately trying to send a ctn that causes an exception, then there is no reason to catch this exception inside AND outside the loop; outside is sufficient.

More generally, I am not convinced this PR is useful at all. If the watchtower has decided to be misbehaving, then it will crash sync_with_remote_watchtower, which is fine IMO. The network taskgroup is shielded with ignore_exceptions.

The only reason to keep sync_with_remote_watchtower alive would be if the user switches to another watchtower while the session is running.

@rdymac

rdymac commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

If switching to another watchtower while the session is running is not easy for the user to do, maybe this overkill is not needed for such edge case.

Comment thread electrum/lnworker.py
Comment on lines +1202 to +1204
if type(watchtower_ctn) is not int or watchtower_ctn < 0:
self.logger.warning(f'watchtower get_ctn returned invalid ctn {watchtower_ctn!r} for {outpoint}')
return

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You could keep only this part so users could do the change on the fly, remove the need to kill the app and start again to then change the watchtower in that new app run.

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