Commit Graph

556 Commits

Author SHA1 Message Date
f321x 1ebb937d46 lnpeer: decode_short_ids: check length of short ids
- if `encoded_short_ids` does not decode into a whole number of `short_channel_id`:
  - MAY send a `warning`.
  - MAY close the connection.
https://github.com/lightning/bolts/blob/0cf21511a781c295b9374aeaef37cf9c4d193502/07-routing-gossip.md?plain=1#L674

# Conflicts:
#	electrum/lnpeer.py
2025-12-11 15:44:11 +01:00
f321x 380d7edea2 lnpeer: remove support for zlib compression
Must not be used anymore according to bolt 7:
https://github.com/lightning/bolts/blob/0cf21511a781c295b9374aeaef37cf9c4d193502/07-routing-gossip.md?plain=1#L600
2025-12-11 15:43:45 +01:00
SomberNight c465f7c3e0 lnworker/lnpeer: don't use lnworker.channels.get(chan_id)
- lnworker.channels takes a copy of the whole dict, to make it thread-safe
- in LNWallet class, can just use self._channels.get(chan_id)
- otherwise there is lnworker.get_channel_by_id
- same for lnpeer.channels.get and lnpeer.get_channel_by_id
2025-12-10 16:14:31 +00:00
ghost43 6ceb4ad71f Merge pull request #10351 from f321x/jit_htlc_switch_fixes
lnpeer/lnutil: fail mpp if we didn't signal mpp in invoice
2025-12-10 15:56:23 +00:00
f321x c34efce984 lnchannel: allow deleting unfunded incoming channels
We tried to delete incoming channels that didn't get funded after
lnutil.CHANNEL_OPENING_TIMEOUT, however an assert prevented this:

```
  3.63 | E | lnwatcher.LNWatcher.[default_wallet-LNW] | Exception in check_onchain_situation: AssertionError()
Traceback (most recent call last):
  File "/home/user/code/electrum-fork/electrum/util.py", line 1233, in wrapper
    return await func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/lnwatcher.py", line 117, in check_onchain_situation
    await self.update_channel_state(
    ...<5 lines>...
        keep_watching=keep_watching)
  File "/home/user/code/electrum-fork/electrum/lnwatcher.py", line 135, in update_channel_state
    chan.update_onchain_state(
    ~~~~~~~~~~~~~~~~~~~~~~~~~^
        funding_txid=funding_txid,
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<2 lines>...
        closing_height=closing_height,
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        keep_watching=keep_watching)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/lnchannel.py", line 341, in update_onchain_state
    self.update_unfunded_state()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/home/user/code/electrum-fork/electrum/lnchannel.py", line 382, in update_unfunded_state
    self.lnworker.remove_channel(self.channel_id)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/lnworker.py", line 3244, in remove_channel
    assert chan.can_be_deleted()
           ~~~~~~~~~~~~~~~~~~~^^
AssertionError
```
2025-12-10 13:20:32 +01:00
f321x 183d426e93 lnpeer: fail htlcs if we get unwanted mpp
Fail incoming htlcs if we receive a payment consisting of multiple parts
if we signaled to not want mpp in the invoice.
2025-12-10 10:36:39 +01:00
f321x 5be598b808 lnworker: use channel_id instead of scid in ReceivedMPPHtlc
Store the channel id instead of the scid in ReceivedMPPHtlc.
The scid can be None, in theory even for multiple channels at the same
time. Using the channel_id which is always available and unique seems
less error prone at the cost of temporarily higher storage requirements
in the db for the duration of the pending htlcs.

Alternatively we could use the local scid alias however using the
channel_id seems less complex and leaves less room for ambiguity.
2025-12-09 14:44:11 +01:00
ThomasV da998150ac lnpeer: deduct JIT fees also for trampoline 2025-12-06 10:33:24 +01:00
f321x fb566eb59e lnpeer: deduct jit channel fees from total amount
Deduct the just in time channel opening fees from the total amount so
htlcs don't get timed out if they come from a just in time channel with
opening fee.

Related: https://github.com/spesmilo/electrum/pull/9584
2025-12-06 10:14:07 +01:00
f321x 923d48f9db lnworker: differentiate PaymentInfo by direction
Allows storing two different payment info of the same payment hash by
including the direction into the db key.
We create and store PaymentInfo for sending attempts and for requests (receiving),
if we try to pay ourself (e.g. through a channel rebalance) the checks
in `save_payment_info` would prevent this and throw an exception.
By storing the PaymentInfos of outgoing and incoming payments separately in
the db this collision is avoided and it makes it easier to reason about
which PaymentInfo belongs where.
2025-12-01 18:39:56 +01:00
f321x 16ed7e666c lnpeer: use INVALID_ONION_VERSION for unparsable onions
Use the `OnionFailureCode.INVALID_ONION_VERSION` (BADONION | PERM | 4)
code when sending back `update_fail_malformed_htlc` as just sending a plain
`BADONION` is not explicitly mentioned as correct in the spec.
2025-11-27 17:58:49 +01:00
f321x abc469c846 lnworker: split dont_settle_htlcs
Splits `LNWallet.dont_settle_htlcs` into `LNWallet.dont_settle_htlcs`
and `LNWallet.dont_expire_htlcs`.

Registering a payment hash in dont_settle_htlcs will prevent it from
getting fulfilled if we have the preimage stored. The preimage will not
be released before the the payment hash gets removed from
dont_settle_htlcs. Htlcs can still get expired as usual or failed if no
preimage is known.
This is only used by Just-in-time channel openings.

Registering a payment hash in dont_expire_htlcs allows to overwrite the
minimum final cltv delta value after which htlcs would usually get
expired. This allows to delay expiry of htlcs or, if the value in the
dont_settle_htlcs dict is None, completely prevent expiry and let the
htlc get expired onchain.

Splitting this up in two different dicts makes it more explicit and
easier to reason about what they are actually doing.

 Please enter the commit message for your changes. Lines starting
2025-11-27 17:58:44 +01:00
f321x 95729a08ef lnpeer: report htlc_switch exceptions to crash reporter
It seems useful to report exceptions happening in the htlc_switch to the
crash reporter as it shouldn't raise exceptions in theory and this could
help catch subtle bugs.
2025-11-27 17:58:41 +01:00
f321x 0f314d1dd9 lnpeer/lnworker: refactor htlc_switch
refactor `htlc_switch` to new architecture to make it more robust
against partial settlement of htlc sets and increase maintainability.
Htlcs are now processed in two steps, first the htlcs are collected into
sets from the channels, and potentially failed on their own already.
Then a second loop iterates over the htlc sets and finalizes only on
whole sets.

# Conflicts:
#	electrum/lnpeer.py
2025-11-27 17:57:14 +01:00
f321x 41d391a617 Fix: For inner trampoline onions amt_to_forward can be larger than the htlc amount
Add unittest to TestPeerForwarding which sends a multi trampoline
payment.

Wait another htlc_switch iteration in tests because trampolines might have different delays
2025-11-27 17:44:31 +01:00
f321x a6e103b63c lnonion:
add helper properties
 minor refactoring
2025-11-25 13:14:57 +01:00
f321x c65ec6b2b9 add helper method: run_htlc_switch_iteration
no code modification, indent-only
2025-11-25 13:14:46 +01:00
ThomasV 077bcf515d StoredDict: use pointers instead of path
Instead of storing its own path, each StoredDict element stores
its own key and a pointer to its parent. If a dict is removed
from the db, its parent pointer is set to None. This makes
self.path return None for all branches that have been pruned.

This passes tests/tests_json_db.py and fixes issue #10000
2025-11-07 10:00:10 +01:00
f321x a5cf5f75fc lnpeer: await init in main_loop
Because `LNPeer.initialized` was awaited in
`LNPeer._query_gossip()` instead of the main loop the other tasks got
spawned concurrently and each task on its own has to wait for the
initialization. In `LNPeer._send_own_gossip()` this was missing, instead
there is a fixed 10 sec sleep. If the connection was not initialized but
the 10 sec are exceeded `_send_own_gossip()` tries to send gossip and
causes this exception as the `LNTransport` is not ready:

```
  2.13 | E | lnpeer.Peer.[LNWallet, 0288fa27c0-bc1900c8] | Exception in main_loop: AttributeError("'LNTransport' object has no attribute 'sk'")
Traceback (most recent call last):
  File "/home/user/code/electrum-fork/electrum/util.py", line 1232, in wrapper
    return await func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 511, in wrapper_func
    return await func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 525, in main_loop
    async with self.taskgroup as group:
               ^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/env/lib/python3.14/site-packages/aiorpcx/curio.py", line 304, in __aexit__
    await self.join()
  File "/home/user/code/electrum-fork/electrum/util.py", line 1420, in join
    task.result()
    ~~~~~~~~~~~^^
  File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 573, in _send_own_gossip
    self.send_node_announcement(alias, color)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 1830, in send_node_announcement
    self.transport.send_bytes(raw_msg)
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/lntransport.py", line 225, in send_bytes
    lc = aead_encrypt(self.sk, self.sn(), b'', l)
                      ^^^^^^^
AttributeError: 'LNTransport' object has no attribute 'sk'. Did you mean: 'sn'?
```

By awaiting the initialization directly in the `main_loop` it is more
clear that the task getting spawned subsequently depend on the transport
being available and separates the initialization more clearly these
other functions.
2025-10-28 15:42:16 +01:00
f321x d62b627a0b lnpeer: move htlc forwarding funcs to lnworker
forwarding happens independent of the peer that received the htlc to
forward and fits better in lnworker.
2025-09-30 09:54:24 +02:00
f321x 32aa6ab20c lnutil: rename RecvMPPResolution.ACCEPTED
Renames RecvMPPResolution.ACCEPTED to .COMPLETE as .ACCEPTED is somewhat
misleading. Accepted could imply that the preimage for this set has been
revealed or that the set has been settled, however it only means that we
have received the full set (it is complete), but the set still can be
failed (e.g. through cltv timeout) and has not been claimed yet.
2025-09-29 16:11:26 +00:00
f321x 7d0a69a9ce lnpeer: only spawn htlc_switch for peers with LNWallet
stop spawning htlc_switch for LNGossip peers, they don't handle any
htlcs
2025-09-29 16:11:20 +00:00
f321x fcc3796079 lnworker: move RecvMPPResolution and status to lnutil
it is required both in lnpeer and lnworker, moving it to lnutil seems to
make more sense.

# Conflicts:
#	electrum/lnworker.py
2025-09-29 16:11:17 +00:00
f321x 9db975f9d7 lightning: remove legacy payment secret derivation
This seems old and not very useful anymore.
2025-09-29 16:11:13 +00:00
f321x acd52da764 lnpeer: cleanup imports 2025-09-29 16:10:59 +00:00
f321x e7bb75bc3e chore: replace calls to asyncio.iscoroutinefunction
Replace calls to deprecated asyncio.iscoroutinefunction with calls to
inspect.iscoroutinefunction to prevent the following deprecation
warnings from showing up if running with Python 3.14:
```
/home/user/code/electrum-fork/electrum/util.py:1225: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead
  assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
/home/user/code/electrum-fork/electrum/util.py:507: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead
  if asyncio.iscoroutinefunction(func):
/home/user/code/electrum-fork/electrum/util.py:1246: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead
  assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
/home/user/code/electrum-fork/electrum/lnpeer.py:272: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead
  assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
/home/user/code/electrum-fork/electrum/util.py:1225: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead
  assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
/home/user/code/electrum-fork/electrum/util.py:507: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead
  if asyncio.iscoroutinefunction(func):
```
2025-09-05 10:29:34 +02:00
SomberNight 835b04d5c6 swaps: add explicit check that (onchain_locktime < LN_locktime)
This was already implicitly checked. This diff makes the check explicit, and serves as an additional sanity-check.
- for client-forward-swaps, we have
  - "cltv safety requirement: (onchain_locktime < LN_locktime),   otherwise client is vulnerable"
  - server chooses onchain locktime delta = 70
    https://github.com/spesmilo/electrum/blob/71255c1e735cdfcd881e045b9f2f6bc6b599f459/electrum/submarine_swaps.py#L701
  - client checks that onchain locktime delta is <100
    https://github.com/spesmilo/electrum/blob/71255c1e735cdfcd881e045b9f2f6bc6b599f459/electrum/submarine_swaps.py#L887
  - client chooses LN locktime delta = 432
    https://github.com/spesmilo/electrum/blob/71255c1e735cdfcd881e045b9f2f6bc6b599f459/electrum/submarine_swaps.py#L907
- for client-reverse-swaps, we have
  - "cltv safety requirement: (onchain_locktime < LN_locktime),   otherwise server is vulnerable"
  - server chooses onchain locktime delta = 70
    https://github.com/spesmilo/electrum/blob/71255c1e735cdfcd881e045b9f2f6bc6b599f459/electrum/submarine_swaps.py#L598
  - server chooses LN locktime delta: unset, i.e. our default of 147
    https://github.com/spesmilo/electrum/blob/71255c1e735cdfcd881e045b9f2f6bc6b599f459/electrum/submarine_swaps.py#L612
    https://github.com/spesmilo/electrum/blob/71255c1e735cdfcd881e045b9f2f6bc6b599f459/electrum/lnworker.py#L2273
2025-08-26 04:55:10 +00:00
SomberNight 1380ed4ba7 lnpeer: rate-limit ordered_message_queues 2025-08-19 18:28:13 +00:00
SomberNight b8d989e13b lnpeer: rate-limit reply_channel_range 2025-08-19 18:28:09 +00:00
SomberNight 65d04dfbb7 lnpeer: slow down peers sending too much gossip 2025-08-19 18:28:01 +00:00
SomberNight f8926b4957 type-hint some Callables
could not figure out how to type-hint coinchooser.sufficient_funds with typing.Protocol,
at least PyCharm complained on all my attempts
2025-08-18 15:38:25 +00:00
f321x 389a0a6e91 cli: use payment hash for add_hold_invoice
Allowing to create hold invoices just by providing a payment hash
instead of the preimage right from the beginning allows for additional
use cases where the recipient doesn't have access to the preimage when
creating the invoice.
2025-06-30 09:34:05 +02:00
ThomasV 853b793bef rm verbosity_shortcuts option (unused, redundant) 2025-05-29 16:20:41 +02:00
SomberNight 65c9a5f875 lnchannel: refactor can_send_ctx_updates
This adds a lot more sanity checks to lnpeer/lnchannel for chan update operations,
and more logging in case they are violated.
2025-05-21 14:57:56 +00:00
SomberNight d0be5fcfc8 lnchannel: persist error sent by remote peer into db
If a force-close happens due to e.g. a feerate disagreement or an invalid signature, etc,
and the remote peer sends us an error, it can be useful if users can provide us with this error.
If the user does not have logging enabled when the error is sent, without this persistence it will likely get lost.
2025-05-18 16:54:56 +00:00
ghost43 79a54c1578 Merge pull request #9826 from f321x/fix_fee_disagreement_ln
fix: reduce update_fee target for anchor channels
2025-05-15 13:30:11 +00:00
f321x 61874e9fe7 fix: reduce update_fee target for anchor channels
the update_fee logic for lightning channels was not adapted to anchor
channels causing us to send update_fee with a eta target of 2 blocks.
This causes force closes when there are mempool spikes as the fees we
try to update to are a lot higher than e.g. eclair uses. Eclair will
force close if our fee is 10x > than their fee.
2025-05-14 18:01:44 +02:00
ThomasV 6d939a0ee6 add LIGHTNING_MAX_HTLC_VALUE_IN_FLIGHT_MSAT to config
This allows to reproduce #9700 using two electrum instances
2025-05-13 13:07:00 +02:00
Sander van Grieken 0ccdddf46a logging levels
lnpeer: received orphan channel update -> debug
exchange_rate: received quotes -> debug
2025-04-24 09:10:59 +02:00
f321x 253ab6849a implement NIP47 plugin 2025-04-10 10:22:29 +02:00
SomberNight b13de0afb5 lnpeer: request_gossip: change log line format
before/after:
`requesting channel graph since Fri Apr  4 17:19:05 2025`
`requesting channel graph since 2025-04-04T17:20:04`
2025-04-04 17:23:26 +00:00
f321x db55e37277 fix exception if LIGHTNING_LISTEN is not set on public node 2025-04-04 12:20:48 +02:00
f321x 0b19b660c5 don't use fallback feerates in lightning by default 2025-04-01 14:12:02 +02:00
ThomasV 58be5a3ad5 Allow wallets to use non-deterministic lightning,
if they use a software keystore.

This excludes hardware wallets and watching-only wallet.
Also, this forbids creation of new channels in those wallets,
in case lightning was previously enabled.

Fixes #9440
2025-03-17 09:51:52 +01:00
ThomasV 764cc78386 Merge pull request #9590 from f321x/jit-update-unfunded-state
Handle unfunded zeroconf channels in update_unfunded_state
2025-03-07 13:55:55 +01:00
f321x 6fd833ccfb add handling of zeroconf channels to update_unfunded_state 2025-03-07 13:53:01 +01:00
ThomasV 8011ae00ab Merge pull request #9586 from f321x/jit-block-preimage
Add mechanism to block htlcs from settling back
2025-03-07 12:04:39 +01:00
f321x 109ad2b3da add mechanism to block htlcs from settling back 2025-03-07 12:03:59 +01:00
ThomasV 840243e029 separate fee policy from config
- Wallet.make_unsigned_transaction takes a FeePolicy parameter
 - fee sliders act on a FeePolicy instead of config
 - different fee policies may be used for different purposes
 - do not detect dust outputs in lnsweep, delegate that to lnwatcher
2025-03-05 10:29:26 +01:00
ThomasV 6234cbf97b Merge pull request #9603 from spesmilo/gossip_info
gossip: less log lines, use command line instead
2025-03-05 08:23:19 +01:00