Skip to content

Commit 8669245

Browse files
committed
Improve JIT channel opening
1 parent cca29ef commit 8669245

File tree

5 files changed

+158
-50
lines changed

5 files changed

+158
-50
lines changed

electrum/lnchannel.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -756,8 +756,8 @@ class Channel(AbstractChannel):
756756
def __repr__(self):
757757
return "Channel(%s)"%self.get_id_for_log()
758758

759-
def __init__(self, state: 'StoredDict', *, name=None, lnworker=None, initial_feerate=None, opening_fee=None):
760-
self.opening_fee = opening_fee
759+
def __init__(self, state: 'StoredDict', *, name=None, lnworker=None, initial_feerate=None, opening_fee_tlv: Optional[dict] = None):
760+
self.opening_fee_tlv = opening_fee_tlv # type: Optional[dict]
761761
self.name = name
762762
self.channel_id = bfh(state["channel_id"])
763763
self.short_channel_id = ShortChannelID.normalize(state["short_channel_id"])

electrum/lnpeer.py

+21-19
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,9 @@ def on_init(self, payload):
393393
if their_networks:
394394
their_chains = list(chunks(their_networks["chains"], 32))
395395
if constants.net.rev_genesis_bytes() not in their_chains:
396-
raise GracefulDisconnect(f"no common chain found with remote. (they sent: {their_chains})")
396+
raise GracefulDisconnect(f"no common chain found with remote. "
397+
f"(they sent: {[chain.hex() for chain in their_chains]}),"
398+
f" our chain: {constants.net.rev_genesis_bytes().hex()}")
397399
# all checks passed
398400
self.lnworker.on_peer_successfully_established(self)
399401
self._received_init = True
@@ -967,7 +969,7 @@ async def channel_establishment_flow(
967969
public: bool,
968970
zeroconf: bool = False,
969971
temp_channel_id: bytes,
970-
opening_fee: int = None,
972+
opening_fee_msat: int = None,
971973
) -> Tuple[Channel, 'PartialTransaction']:
972974
"""Implements the channel opening flow.
973975
@@ -1033,10 +1035,10 @@ async def channel_establishment_flow(
10331035
open_channel_tlvs['upfront_shutdown_script'] = {
10341036
'shutdown_scriptpubkey': local_config.upfront_shutdown_script
10351037
}
1036-
if opening_fee:
1038+
if opening_fee_msat:
10371039
# todo: maybe add payment hash
10381040
open_channel_tlvs['channel_opening_fee'] = {
1039-
'channel_opening_fee': opening_fee
1041+
'channel_opening_fee': opening_fee_msat
10401042
}
10411043
# for the first commitment transaction
10421044
per_commitment_secret_first = get_per_commitment_secret_from_seed(
@@ -1269,10 +1271,13 @@ async def on_open_channel(self, payload):
12691271
# store the temp id now, so that it is recognized for e.g. 'error' messages
12701272
# TODO: this is never cleaned up; the dict grows unbounded until disconnect
12711273
self.temp_id_to_id[temp_chan_id] = None
1272-
channel_opening_fee = open_channel_tlvs.get('channel_opening_fee') if open_channel_tlvs else None
1273-
if channel_opening_fee:
1274-
# todo check that the fee is reasonable
1275-
pass
1274+
channel_opening_fee_tlv = open_channel_tlvs.get('channel_opening_fee') if open_channel_tlvs else None # type: Optional[dict]
1275+
if channel_opening_fee_tlv:
1276+
channel_opening_fee_msat = channel_opening_fee_tlv['channel_opening_fee']
1277+
# reject channel if fee is > 10% of funding amount (e.g. >40k sat on 400k incoming channel)
1278+
# the opening fee depends on the LSP and mempool situation
1279+
if channel_opening_fee_msat // 1000 > funding_sat * 0.1:
1280+
raise Exception(f"Channel opening fee is too expensive, rejecting channel")
12761281

12771282
if self.use_anchors():
12781283
multisig_funding_keypair = lnutil.derive_multisig_funding_key_if_they_opened(
@@ -1386,7 +1391,7 @@ async def on_open_channel(self, payload):
13861391
chan_dict,
13871392
lnworker=self.lnworker,
13881393
initial_feerate=feerate,
1389-
opening_fee = channel_opening_fee,
1394+
opening_fee_tlv = channel_opening_fee_tlv,
13901395
)
13911396
chan.storage['init_timestamp'] = int(time.time())
13921397
if isinstance(self.transport, LNTransport):
@@ -2244,7 +2249,7 @@ async def maybe_forward_trampoline(
22442249

22452250
# do we have a connection to the node?
22462251
next_peer = self.lnworker.peers.get(outgoing_node_id)
2247-
if next_peer and next_peer.accepts_zeroconf():
2252+
if next_peer and next_peer.accepts_zeroconf() and self.lnworker.features.supports(LnFeatures.OPTION_ZEROCONF_OPT):
22482253
self.logger.info(f'JIT: found next_peer')
22492254
for next_chan in next_peer.channels.values():
22502255
if next_chan.can_pay(amt_to_forward):
@@ -2356,8 +2361,8 @@ def check_accepted_htlc(
23562361
log_fail_reason(f"'total_msat' missing from onion")
23572362
raise exc_incorrect_or_unknown_pd
23582363

2359-
if chan.opening_fee:
2360-
channel_opening_fee = chan.opening_fee['channel_opening_fee']
2364+
if chan.opening_fee_tlv:
2365+
channel_opening_fee = chan.opening_fee_tlv['channel_opening_fee']
23612366
total_msat -= channel_opening_fee
23622367
amt_to_forward -= channel_opening_fee
23632368
else:
@@ -2411,7 +2416,7 @@ def maybe_fulfill_htlc(
24112416
Return (preimage, (payment_key, callback)) with at most a single element not None.
24122417
"""
24132418
if not processed_onion.are_we_final:
2414-
if not self.lnworker.enable_htlc_forwarding:
2419+
if not self.lnworker.enable_htlc_forwarding or already_forwarded:
24152420
return None, None
24162421
# use the htlc key if we are forwarding
24172422
payment_key = serialize_htlc_key(chan.get_scid_or_local_alias(), htlc.htlc_id)
@@ -2498,7 +2503,7 @@ def log_fail_reason(reason: str):
24982503
log_fail_reason(f"no payment_info found for RHASH {htlc.payment_hash.hex()}")
24992504
raise exc_incorrect_or_unknown_pd
25002505

2501-
preimage = self.lnworker.get_preimage(payment_hash)
2506+
preimage = self.lnworker.get_preimage(payment_hash, only_settleable=True)
25022507
expected_payment_secrets = [self.lnworker.get_payment_secret(htlc.payment_hash)]
25032508
if preimage:
25042509
expected_payment_secrets.append(derive_payment_secret_from_payment_preimage(preimage)) # legacy secret for old invoices
@@ -2525,10 +2530,7 @@ def log_fail_reason(reason: str):
25252530
else:
25262531
return None, None
25272532

2528-
if payment_hash.hex() in self.lnworker.dont_settle_htlcs:
2529-
return None, None
2530-
2531-
chan.opening_fee = None
2533+
chan.opening_fee_tlv = None
25322534
self.logger.info(f"maybe_fulfill_htlc. will FULFILL HTLC: chan {chan.short_channel_id}. htlc={str(htlc)}")
25332535
return preimage, None
25342536

@@ -3120,7 +3122,7 @@ async def wrapped_callback():
31203122
# HTLC we are supposed to forward, and have already forwarded
31213123
# for final trampoline onions, forwarding failures are stored with forwarding_key (which is the inner key)
31223124
payment_key = forwarding_key
3123-
preimage = self.lnworker.get_preimage(payment_hash)
3125+
preimage = self.lnworker.get_preimage(payment_hash, only_settleable=True)
31243126
error_bytes, error_reason = self.lnworker.get_forwarding_failure(payment_key)
31253127
if error_bytes:
31263128
return None, None, error_bytes

electrum/lnwatcher.py

+10
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ def stop(self):
4141
def get_channel_status(self, outpoint):
4242
return self.channel_status.get(outpoint, 'unknown')
4343

44+
def add_channel(self, outpoint: str, address: str) -> None:
45+
assert isinstance(outpoint, str)
46+
assert isinstance(address, str)
47+
cb = lambda: self.check_onchain_situation(address, outpoint)
48+
self.add_callback(address, cb)
49+
50+
def unwatch_channel(self, address, funding_outpoint):
51+
self.logger.info(f'unwatching {funding_outpoint}')
52+
self.remove_callback(address)
53+
4454
def remove_callback(self, address):
4555
self.callbacks.pop(address, None)
4656

0 commit comments

Comments
 (0)