Commit Graph

400 Commits

Author SHA1 Message Date
ThomasV
4b29a46890 lnpeer: fix logging of 'will fullfill htlc' 2023-07-09 10:06:46 +02:00
SomberNight
fc6486ecdb lnaddr: make payment_secret field mandatory, in both directions
we now require payment_secret both for sending and for receiving
(previously was optional for both)

see
https://github.com/lightning/bolts/pull/898
https://github.com/ACINQ/eclair/pull/1810
https://github.com/ElementsProject/lightning/pull/4646

note: payment_secret depends on var_onion_optin, so that becomes mandatory as well,
however this commit does not yet remove the ability of creating legacy onions
2023-06-29 14:34:02 +00:00
ThomasV
df5b98792e lnworker: always call check_received_htlc (no only for MPP)
This will be a generic placeholder to decide if we need to wait
before settling a payment (to be used with hold invoices and
bundled payments)
2023-06-26 09:29:40 +02:00
ThomasV
21e06b7065 lnpeer: new payment secret, derived without preimage.
(this is needed for hold invoices)
2023-06-25 19:15:52 +02:00
SomberNight
ca93af2b8a ln: some clean-up for option_scid_alias
- qt chan details dlg: show both local and remote aliases
- lnchannel: more descriptive names, add clarification in doctstrings,
  and also save the "local_scid_alias" in the wallet file (to remember if
  we sent it)
- lnpeer:
  - resend channel_ready msg after reestablish, to upgrade old existing channels
    to having local_scid_alias
  - forwarding bugfix, to follow BOLT-04:
    > - if it returns a `channel_update`:
    >   - MUST set `short_channel_id` to the `short_channel_id` used by the incoming onion.
2023-06-23 19:51:57 +00:00
SomberNight
922981e586 lnpeer: improve logging in maybe_forward_htlc 2023-06-21 15:22:17 +00:00
SomberNight
24980feab7 config: introduce ConfigVars
A new config API is introduced, and ~all of the codebase is adapted to it.
The old API is kept but mainly only for dynamic usage where its extra flexibility is needed.

Using examples, the old config API looked this:
```
>>> config.get("request_expiry", 86400)
604800
>>> config.set_key("request_expiry", 86400)
>>>
```

The new config API instead:
```
>>> config.WALLET_PAYREQ_EXPIRY_SECONDS
604800
>>> config.WALLET_PAYREQ_EXPIRY_SECONDS = 86400
>>>
```

The old API operated on arbitrary string keys, the new one uses
a static ~enum-like list of variables.

With the new API:
- there is a single centralised list of config variables, as opposed to
  these being scattered all over
- no more duplication of default values (in the getters)
- there is now some (minimal for now) type-validation/conversion for
  the config values

closes https://github.com/spesmilo/electrum/pull/5640
closes https://github.com/spesmilo/electrum/pull/5649

Note: there is yet a third API added here, for certain niche/abstract use-cases,
where we need a reference to the config variable itself.
It should only be used when needed:
```
>>> var = config.cv.WALLET_PAYREQ_EXPIRY_SECONDS
>>> var
<ConfigVarWithConfig key='request_expiry'>
>>> var.get()
604800
>>> var.set(3600)
>>> var.get_default_value()
86400
>>> var.is_set()
True
>>> var.is_modifiable()
True
```
2023-05-25 17:39:48 +00:00
SomberNight
6fade55dd6 bolts: do not disconnect when receiving/sending "warning" messages
follow https://github.com/lightning/bolts/pull/1075
2023-05-10 12:22:48 +00:00
SomberNight
c9536180c5 lnutil.LnFeatures: limit max feature bit to 10_000
closes https://github.com/spesmilo/electrum/issues/8403

> In Python 3.10 that worked fine, however in Python 3.11 large integer check https://github.com/python/cpython/issues/95778, so now this throws an error.

Apparently this change was deemed a security fix and was backported to all supported branches of CPython (going back to 3.7). i.e. it affects ~all versions of python (if sufficiently updated with bugfix patches), not just 3.11

> Some offending node aliases:
> ```
> ergvein-fiatchannels
> test-mainnet
> arakis
> ```

The features bits set by some of these nodes:
```
(1, 7, 8, 11, 13, 14, 17, 19, 23, 27, 45, 32973, 52973)
(1, 7, 8, 11, 13, 14, 17, 19, 23, 27, 39, 45, 55, 32973, 52973)
```

> P.S. I see there are a lot of nodes with 253 bytes in their feature vectors. Any idea why that could happen?

Note that the valid [merged-into-spec features](50b2df24a2/09-features.md) currently only go as high as ~51.
However the spec does not specify how to choose feature bits for experimental stuff, so I guess some people are using values in the 50k range. The only limit imposed by the spec on the length of the features bitvector is an implicit one due to the max message size: every msg must be smaller than 65KB, and the features bitvector needs to fit inside the init message, hence it can be up to ~524K bits.
(note that the features are not stored in a sparse representation in the init message and in gossip messages, so if many nodes set such high feature bits, that would noticably impact the size of the gossip).

-----

Anyway, our current implementation of LnFeatures is subclassing IntFlag, and it looks like it does not work well for such large integers. I've managed to make IntFlags reasonably in python 3.11 by overriding __str__ and __repr__ (note that in cpython it is apparently only the base2<->base10 conversions that are slow, power-of-2 conversions are fast, so we can e.g. use `hex()`). However in python 3.10 and older, enum.py itself seems really slow for bigints, e.g. enum._decompose in python 3.10.

Try e.g. this script, which is instant in py3.11 but takes minutes in py3.10:
```py
from enum import IntFlag
class c(IntFlag):
    known_flag_1 = 1 << 0
    known_flag_2 = 1 << 1
    known_flag_3 = 1 << 2
    if hasattr(IntFlag, "_numeric_repr_"):  # python 3.11+
        _numeric_repr_ = hex
    def __repr__(self):
        return f"<{self._name_}: {hex(self._value_)}>"
    def __str__(self):
        return hex(self._value_)

a = c(2**70000-1)
q1 = repr(a)
q2 = str(a)
```

AFAICT we have two options: either we rewrite LnFeatures so that it does not use IntFlag (and enum.py), or, for the short term as workaround, we could just reject very large feature bits.
For now, I've opted to the latter, rejecting feature bits over 10k.

(note that another option is bumping the min required python to 3.11, in which case with the overrides added in this commit the performance looks perfectly fine)
2023-05-08 19:37:33 +00:00
SomberNight
312f2641e7 don't use bare except
use "except Exception", or if really needed explicitly "except BaseException"
2023-04-24 12:58:01 +00:00
SomberNight
72da9c1a6a sanitise untrusted error bytes before logging it
full-blown paranoia kicking in
2023-04-06 14:28:31 +00:00
SomberNight
84d19457a6 lnpeer: handle NoDynamicFeeEstimates in co-op close
note that the existing fallback was insufficient as config.fee_per_kb() can still return None
2023-03-30 15:40:45 +00:00
SomberNight
373db76ac9 util: kill bh2u
no longer useful, and the name is so confusing...
2023-02-17 11:43:11 +00:00
SomberNight
1ce37c8bb1 transaction: rm hardcoded sighash magic numbers 2023-02-17 11:40:12 +00:00
SomberNight
faea1e6e1a lnchannel: add more debug logging for ctx/htlc sigs
related: https://github.com/spesmilo/electrum/issues/8191
2023-02-13 01:23:47 +00:00
SomberNight
046609c5d2 lnpeer: add note about thread-safety, and some checks
I was calling methods from the Qt console (e.g. peer.pay()) and seeing weird behaviour...
htlc_switch() (running on asyncio thread) was racing with pay() (running on GUI thread).
2023-02-05 22:49:12 +00:00
ThomasV
b9393b0603 Support scid alias:
- save remote alias for use in invoices
 - derive local alias from wallet xpub
 - send channel_type without the option_scid_alias bit
   (apparently LND does not like it)
2023-01-13 15:47:30 +01:00
ThomasV
c109d5e722 lnwire: update csv files with recent BOLTs
Note: there are no more optional fields in msgdata, per f068dd0d8d
2023-01-13 12:50:48 +01:00
SomberNight
ab953f4a7f lnmsg: add details to FailedToParseMsg, log message type
note: Would it be ok to log potentially secret (semi-sensitive) data?
We take care not to log onchain private keys as they are extremely sensitive,
but what about logging a LN transport message that might contain channel secrets?
Decided not to, for now.
2023-01-13 10:37:06 +00:00
ThomasV
e8a4e287e9 cleanup old non-static_remotekey code (follow-up 1f403d1ca1) 2022-09-23 11:25:49 +02:00
ThomasV
2af59e32b2 lnworker: define use_trampoline() for code clarity 2022-09-19 17:43:13 +02:00
ThomasV
1f403d1ca1 remove support for channels without static remote pubkey 2022-08-16 08:48:59 +02:00
ThomasV
a5965933d2 Fix CTNs in should_be_closed_due_to_expiring_htlcs (fixes #7906).
Also fix sending too many fee updates.
Rename lnworker.on_channel_update, that name was misleading.
2022-08-02 18:00:39 +02:00
ThomasV
dce39b38ce lnchannel: do not expose COOP_CLOSE in the GUI if there are unsettled HTLCs 2022-07-08 12:27:04 +02:00
SomberNight
c1d34243a1 lnpeer: (trivial) better log message 2022-06-28 20:34:47 +02:00
SomberNight
1613736b45 lnpeer: rename trigger_force_close to request_force_close
for more consistent naming with rest of the code
2022-06-10 17:13:11 +02:00
SomberNight
f12e87be93 lnchannel: add new states: WE_ARE_TOXIC, REQUESTED_FCLOSE
The `WE_ARE_TOXIC` state is added as a sanity check to ensure that if
the remote has proven that we have lost state we do not accidentally
do a local force-close. E.g. if we receive an "error" message for the
channel, we might normally do an automatic force-close. Manually
force-closing in such a state is not offered anymore by the GUI.

The `REQUESTED_FCLOSE` state is added as it is quite likely that
we receive an error message from the remote after requesting a fclose,
e.g. during a later chan-reestablish. In such a scenario, we should
not do an auto-local-fclose, however the manual option of a local-fclose
should still be offered.
2022-06-10 17:09:33 +02:00
ThomasV
121d8732f1 Persist LNWatcher transactions in wallet file:
- separate AddressSynchronizer from Wallet and LNWatcher
 - the AddressSynchronizer class is referred to as 'adb' (address database)
 - Use callbacks to replace overloaded methods
2022-06-10 13:07:53 +02:00
SomberNight
90dbac5a65 lnpeer: make "trigger_force_close" work with eclair 0.7+ remotes 2022-06-07 19:53:27 +02:00
SomberNight
06687e06d8 lnpeer: forwarding: better handle if next_peer is offline 2022-05-30 16:46:31 +02:00
ThomasV
cc1b4a5c90 lnpeer: fix ping behavior.
- Do not send ping if messages have been received recently.
 - Do not send more than one ping.
 - Await pong before sending commitment_signed (per BOLT-2)
 - Lower ping time to 30s
2022-05-30 09:31:42 +02:00
SomberNight
ef4477a930 lnpeer.reestablish_chan: enforce order of replaying commitsig/revack
When replaying messages during channel-reestablishment,
previously we first resent all update messages, along with potential commitment_signed messages,
and then we potentially resent a single revoke_and_ack.

This can result in incorrect behaviour in case both a commitment_signed and a revoke_and_ack needs to be resent.
When replaying messages, the relative order of commitment_signed and revoke_and_messages needs to be preserved.
(the order of updates (htlc/fee) in relation to the revack messages does not matter)

implements https://github.com/lightning/bolts/pull/810

The logic here is somewhat based on what c-lightning does:
01e5f1886e/channeld/channeld.c (L3059)
2022-05-25 19:44:44 +02:00
ThomasV
917f256e33 remove scheduled invoices: bad UX. better expect the user to retry later. 2022-05-21 12:24:26 +02:00
ThomasV
cbeea6e42a fix #7783 2022-04-27 10:06:05 +02:00
SomberNight
b7dd51612e asyncio: use loop.create_future() instead of asyncio.Future()
from https://docs.python.org/3.10/library/asyncio-future.html#asyncio.Future :
> the recommended way to create a Future object is to call loop.create_future().
> This way alternative event loop implementations can inject their own optimized implementations of a Future object.
2022-04-26 20:24:04 +02:00
ThomasV
60865f3902 Show options if we do not have the liquidity to pay a lightning invoice:
pay onchain, open channel, rebalance.

If we do a swap or open a channel, the payment will be scheduled.
2022-04-20 12:48:22 +02:00
SomberNight
a92dede490 lnpeer: some rework of error/warning message handling
- rm the `_get_channel_ids` abstraction as each of its usages needs subtle differences.
  Some code duplication is preferable in this case.
- raise exceptions in `wait_for_message`, so that callers such as the GUI can show user-feedback
- on_error/on_warning were dropping messages with temp_chan_ids if they were not stored in
  `temp_id_to_id` - which was only done once the mapping was known (so the normal chan_id was known).
  To fix this, we now store temp_chan_ids into `temp_id_to_id` early.
- `schedule_force_closing` only works if the chan_id is already in `channels`

related:
https://github.com/spesmilo/electrum/pull/7645 (and related commits)

-----

example before commit:
```
D/P | lnpeer.Peer.[LNWallet, 03933884aa-3b53e4ab] | Sending OPEN_CHANNEL
D/P | lnpeer.Peer.[LNWallet, 03933884aa-3b53e4ab] | Received ERROR
I/P | lnpeer.Peer.[LNWallet, 03933884aa-3b53e4ab] | remote peer sent error [DO NOT TRUST THIS MESSAGE]: invalid funding_satoshis=10000 sat (min=400000 sat max=1500000000 sat)

E | gui.qt.main_window.[test_segwit_2] | Could not open channel
Traceback (most recent call last):
  File "...\electrum\electrum\util.py", line 1160, in wrapper
    return await func(*args, **kwargs)
  File "...\electrum\electrum\lnpeer.py", line 661, in wrapper
    return await func(self, *args, **kwargs)
  File "...\electrum\electrum\lnpeer.py", line 742, in channel_establishment_flow
    payload = await self.wait_for_message('accept_channel', temp_channel_id)  #
  File "...\electrum\electrum\lnpeer.py", line 315, in wait_for_message
    name, payload = await asyncio.wait_for(q.get(), LN_P2P_NETWORK_TIMEOUT)
  File "...\Python39\lib\asyncio\tasks.py", line 468, in wait_for
    await waiter
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "...\Python39\lib\asyncio\tasks.py", line 492, in wait_for
    fut.result()
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "...\electrum\electrum\gui\qt\util.py", line 914, in run
    result = task.task()
  File "...\electrum\electrum\gui\qt\main_window.py", line 1875, in task
    return self.wallet.lnworker.open_channel(
  File "...\electrum\electrum\lnworker.py", line 1075, in open_channel
    chan, funding_tx = fut.result()
  File "...\Python39\lib\concurrent\futures\_base.py", line 445, in result
    return self.__get_result()
  File "...\Python39\lib\concurrent\futures\_base.py", line 390, in __get_result
    raise self._exception
  File "...\electrum\electrum\util.py", line 1160, in wrapper
    return await func(*args, **kwargs)
  File "...\electrum\electrum\lnworker.py", line 1006, in _open_channel_coroutine
    chan, funding_tx = await asyncio.wait_for(coro, LN_P2P_NETWORK_TIMEOUT)
  File "...\Python39\lib\asyncio\tasks.py", line 494, in wait_for
    raise exceptions.TimeoutError() from exc
asyncio.exceptions.TimeoutError
```

example after commit:
```
D/P | lnpeer.Peer.[LNWallet, 03933884aa-ff3a866f] | Sending OPEN_CHANNEL
D/P | lnpeer.Peer.[LNWallet, 03933884aa-ff3a866f] | Received ERROR
I/P | lnpeer.Peer.[LNWallet, 03933884aa-ff3a866f] | remote peer sent error [DO NOT TRUST THIS MESSAGE]: invalid funding_satoshis=10000 sat (min=400000 sat max=1500000000 sat). chan_id=124ca21fa6aa2993430ad71f465f0d44731ef87f7478e4b31327e4459b5a3988
E | lnworker.LNWallet.[test_segwit_2] | Exception in _open_channel_coroutine: GracefulDisconnect('remote peer sent error [DO NOT TRUST THIS MESSAGE]: invalid funding_satoshis=10000 sat (min=400000 sat max=1500000000 sat)')
Traceback (most recent call last):
  File "...\electrum\electrum\util.py", line 1160, in wrapper
    return await func(*args, **kwargs)
  File "...\electrum\electrum\lnworker.py", line 1006, in _open_channel_coroutine
    chan, funding_tx = await asyncio.wait_for(coro, LN_P2P_NETWORK_TIMEOUT)
  File "...\Python39\lib\asyncio\tasks.py", line 481, in wait_for
    return fut.result()
  File "...\electrum\electrum\lnpeer.py", line 673, in wrapper
    return await func(self, *args, **kwargs)
  File "...\electrum\electrum\lnpeer.py", line 755, in channel_establishment_flow
    payload = await self.wait_for_message('accept_channel', temp_channel_id)
  File "...\electrum\electrum\lnpeer.py", line 326, in wait_for_message
    raise GracefulDisconnect(
electrum.interface.GracefulDisconnect: remote peer sent error [DO NOT TRUST THIS MESSAGE]: invalid funding_satoshis=10000 sat (min=400000 sat max=1500000000 sat)
I/P | lnpeer.Peer.[LNWallet, 03933884aa-ff3a866f] | Disconnecting: GracefulDisconnect()
```
2022-03-17 17:52:38 +01:00
ThomasV
05119b0d2c lnpeer: fix shutdown: do not broadcast dummy tx 2022-03-15 09:15:48 +01:00
ThomasV
a65b97d25c lnpeer: raise GracefulDisconnect in wait_for_message 2022-03-10 14:56:46 +01:00
ThomasV
00902a40d7 lnpeer: favor schedule_force_closing wrapper 2022-03-09 15:53:53 +01:00
ThomasV
da16b11159 minor: simplification 2022-03-09 15:46:42 +01:00
ThomasV
61eebb2b77 lnpeer: no need to raise GracefulDisconnect twice 2022-03-09 13:48:11 +01:00
ThomasV
47917d9e6c lnpeer: factorize on_warning/on_error code 2022-03-09 13:40:44 +01:00
bitromortac
3915045067 lnpeer: warnings for shutdown and open_channel 2022-03-09 13:40:44 +01:00
bitromortac
9e800172ec lnpeer: send/handle error and warning messages
* adds methods for sending protocol errors/warnings
* handling of warning messages
2022-03-09 13:40:44 +01:00
ThomasV
6667a79f10 modern shutdown:
- clarify TODOs
 - add tests for shutdown with modern negotiation
2022-03-08 11:57:19 +01:00
ThomasV
0b203f0b94 lnpeer: refactor fee negotiation in _shutdown
- the fee negotiation is split into smaller functions, reducing the scope of variables.
  - the while loop logic is condensed in a few lines, so it is easier to understand termination conditions.
  - removed code that was never executed
2022-03-08 11:55:40 +01:00
bitromortac
ec740d45f1 lnpeer: modern fee negotiation
Updates the closing fee negotiation to comply with most recent spec
changes, see https://github.com/lightning/bolts/pull/847
The closing negotiation is backwards compatible with the old
negotiation.
2022-03-07 10:36:20 +01:00
SomberNight
556b98736e lnworker.try_force_closing: changed to not be async (and renamed)
This is to ensure that the channel is "immediately" set to FORCE_CLOSING.
(previously it took at least one event loop iteration)
2022-02-21 18:09:45 +01:00
ThomasV
b268877d53 Merge pull request #7636 from bitromortac/2201-channel-type
lightning: implement channel types
2022-02-21 12:08:54 +01:00