- introduce PaymentFeeBudget, which contains limits for fee budget and cltv budget
- when splitting a payment,
- the fee budget is linearly distributed between the parts
- this resolves a FIXME in lnrouter ("FIXME in case of MPP")
- the cltv budget is simply copied
- we could also add other kinds of budgets later, e.g. for the num in-flight htlcs
- resolves TODO in lnworker ("todo: compare to the fee of the actual route we found")
The convention is that edges (start_node -> edge_node) store
the policy/fees for the *start_node*.
This is what the non-trampoline edges were already using (for a long time),
but the trampoline ones were off-by-one (policy was for end_node),
which was then worked around in multiple places, to correct for...
i.e. I think because of all the workarounds, there was no actual bug,
but it was just very confusing.
Also note that the prior usage of trampoline edges would not work if
we (sender) were not directly connected to a TF (trampoline-forwarder)
but had extra edges in the route to even get to the first TF.
Having the policy corresponding to the start_node of the edge would work
even in that case.
Note: this issue is currently not detected in python unittests,
it shows up only in regtest, and is not currently tested.
One would need to use a proper LNWallet instance in unit tests.
- Refactor _run_mpp so that it takes only one set of arguments.
Success and failure conditions are now tested by calling
_run_mpp multiple times.
- In test_payment_multipart with_timeout, check that the received
failure message actually is MPP_TIMEOUT, and not a generic
failure. Since the onion is obfuscated by the forwarding node,
this tests that obfuscate_onion_packet and decode_onion_packet
work as expected.
- is_trampoline was True iff we are the final recipient of a trampoline payment
- in that case, we were only comparing htlc.cltv_expiry against the outer onion cltv
- we should and do now also compare against the inner onion cltv
- the checks are changed from "!=" to "<", to adapt to bolts PR 1032
- see b38156b951
- note that the leniency is needed for the cltv off-by-one
added in eca10eb04d to work
propageate back to the sender.
lnworker: in htlc_fulfilled and htlc_failed, return early if the
htlc was forwarded, so that we do not trigger invoice callbacks
lnworker.pay_to_node(min_cltv_expiry=) expects a relative cltv, and
we were passing an absolute one.
It is not so clear what precisely should be passed here, and that is
because pay_to_node's API was not written with forwarding in mind.
The value chosen here is just some guess that typically should work.
catch OSError for proxy-related issues (and probably other low level networking)
```
19.52 | E | exchange_rate.CoinGecko | failed fx quotes: ProxyConnectionError(22, 'Can not connect to proxy localhost:9050 [The remote computer refused the network connection]')
Traceback (most recent call last):
File "...\Python310\site-packages\python_socks\async_\asyncio\ext\_proxy.py", line 59, in _connect
await self._stream.open_connection(
File "...\Python310\site-packages\python_socks\async_\asyncio\ext\_stream.py", line 61, in open_connection
self._reader, self._writer = await asyncio.open_connection(
File "...\Python310\lib\asyncio\streams.py", line 48, in open_connection
transport, _ = await loop.create_connection(
File "...\Python310\lib\asyncio\base_events.py", line 1076, in create_connection
raise exceptions[0]
File "...\Python310\lib\asyncio\base_events.py", line 1060, in create_connection
sock = await self._connect_sock(
File "...\Python310\lib\asyncio\base_events.py", line 969, in _connect_sock
await self.sock_connect(sock, address)
File "...\Python310\lib\asyncio\proactor_events.py", line 709, in sock_connect
return await self._proactor.connect(sock, address)
File "...\Python310\lib\asyncio\windows_events.py", line 826, in _poll
value = callback(transferred, key, ov)
File "...\Python310\lib\asyncio\windows_events.py", line 613, in finish_connect
ov.getresult()
ConnectionRefusedError: [WinError 1225] The remote computer refused the network connection
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "...\electrum\exchange_rate.py", line 85, in update_safe
self._quotes = await self.get_rates(ccy)
File "...\electrum\exchange_rate.py", line 345, in get_rates
json = await self.get_json('api.coingecko.com', '/api/v3/exchange_rates')
File "...\electrum\exchange_rate.py", line 69, in get_json
async with session.get(url) as response:
File "...\Python310\site-packages\aiohttp\client.py", line 1140, in __aenter__
self._resp = await self._coro
File "...\Python310\site-packages\aiohttp\client.py", line 535, in _request
conn = await self._connector.connect(
File "...\Python310\site-packages\aiohttp\connector.py", line 543, in connect
proto = await self._create_connection(req, traces, timeout)
File "...\Python310\site-packages\aiohttp\connector.py", line 906, in _create_connection
_, proto = await self._create_direct_connection(req, traces, timeout)
File "...\Python310\site-packages\aiohttp\connector.py", line 1174, in _create_direct_connection
transp, proto = await self._wrap_create_connection(
File "...\Python310\site-packages\aiohttp_socks\connector.py", line 58, in _wrap_create_connection
stream = await proxy.connect(
File "...\Python310\site-packages\python_socks\async_\asyncio\ext\_proxy.py", line 47, in connect
await self._connect()
File "...\Python310\site-packages\python_socks\async_\asyncio\ext\_proxy.py", line 73, in _connect
raise ProxyConnectionError(e.errno, msg) from e
python_socks._errors.ProxyConnectionError: [Errno 22] Can not connect to proxy localhost:9050 [The remote computer refused the network connection]
```
- construct_channel_announcement: return also whether
node ids are in reverse order
- maybe_send_channel_announcement:
return early if signatures have not been received
- see comment in lnaddr.py
- Previously we used feature bit 50/51 for trampoline.
The spec subsequently defined fbit 50/51 as option_zeroconf, which
requires fbit 46/47 (option_scid_alias) to also be set.
We moved the non-standard trampoline fbit to a different int.
However, old wallets might have old invoices saved that set fbit 50/51
for trampoline, and those would not have the dependent bit set.
Invoices are parsed at wallet-open, so if the parser ran these checks,
those wallets could not be opened.
- note: we could potentially also run lnaddr.validate_and_compare_features
when saving new invoices into the wallet but this is not done here