Skip to content

Commit d0128b6

Browse files
committed
Improve JIT channel opening
1 parent 7030f3d commit d0128b6

File tree

6 files changed

+147
-54
lines changed

6 files changed

+147
-54
lines changed

electrum/lnchannel.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -748,8 +748,8 @@ class Channel(AbstractChannel):
748748
def __repr__(self):
749749
return "Channel(%s)"%self.get_id_for_log()
750750

751-
def __init__(self, state: 'StoredDict', *, name=None, lnworker=None, initial_feerate=None, opening_fee=None):
752-
self.opening_fee = opening_fee
751+
def __init__(self, state: 'StoredDict', *, name=None, lnworker=None, initial_feerate=None, opening_fee_tlv: Optional[dict] = None):
752+
self.opening_fee_tlv = opening_fee_tlv # type: Optional[dict]
753753
self.name = name
754754
self.channel_id = bfh(state["channel_id"])
755755
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
@@ -1026,10 +1028,10 @@ async def channel_establishment_flow(
10261028
open_channel_tlvs['upfront_shutdown_script'] = {
10271029
'shutdown_scriptpubkey': local_config.upfront_shutdown_script
10281030
}
1029-
if opening_fee:
1031+
if opening_fee_msat:
10301032
# todo: maybe add payment hash
10311033
open_channel_tlvs['channel_opening_fee'] = {
1032-
'channel_opening_fee': opening_fee
1034+
'channel_opening_fee': opening_fee_msat
10331035
}
10341036
# for the first commitment transaction
10351037
per_commitment_secret_first = get_per_commitment_secret_from_seed(
@@ -1258,10 +1260,13 @@ async def on_open_channel(self, payload):
12581260
# store the temp id now, so that it is recognized for e.g. 'error' messages
12591261
# TODO: this is never cleaned up; the dict grows unbounded until disconnect
12601262
self.temp_id_to_id[temp_chan_id] = None
1261-
channel_opening_fee = open_channel_tlvs.get('channel_opening_fee') if open_channel_tlvs else None
1262-
if channel_opening_fee:
1263-
# todo check that the fee is reasonable
1264-
pass
1263+
channel_opening_fee_tlv = open_channel_tlvs.get('channel_opening_fee') if open_channel_tlvs else None # type: Optional[dict]
1264+
if channel_opening_fee_tlv:
1265+
channel_opening_fee_msat = channel_opening_fee_tlv['channel_opening_fee']
1266+
# reject channel if fee is > 10% of funding amount (e.g. >40k sat on 400k incoming channel)
1267+
# the opening fee depends on the LSP and mempool situation
1268+
if channel_opening_fee_msat // 1000 > funding_sat * 0.1:
1269+
raise Exception(f"Channel opening fee is too expensive, rejecting channel")
12651270

12661271
if self.use_anchors():
12671272
multisig_funding_keypair = lnutil.derive_multisig_funding_key_if_they_opened(
@@ -1375,7 +1380,7 @@ async def on_open_channel(self, payload):
13751380
chan_dict,
13761381
lnworker=self.lnworker,
13771382
initial_feerate=feerate,
1378-
opening_fee = channel_opening_fee,
1383+
opening_fee_tlv = channel_opening_fee_tlv,
13791384
)
13801385
chan.storage['init_timestamp'] = int(time.time())
13811386
if isinstance(self.transport, LNTransport):
@@ -2221,7 +2226,7 @@ async def maybe_forward_trampoline(
22212226

22222227
# do we have a connection to the node?
22232228
next_peer = self.lnworker.peers.get(outgoing_node_id)
2224-
if next_peer and next_peer.accepts_zeroconf():
2229+
if next_peer and next_peer.accepts_zeroconf() and self.lnworker.features.supports(LnFeatures.OPTION_ZEROCONF_OPT):
22252230
self.logger.info(f'JIT: found next_peer')
22262231
for next_chan in next_peer.channels.values():
22272232
if next_chan.can_pay(amt_to_forward):
@@ -2333,8 +2338,8 @@ def check_accepted_htlc(
23332338
log_fail_reason(f"'total_msat' missing from onion")
23342339
raise exc_incorrect_or_unknown_pd
23352340

2336-
if chan.opening_fee:
2337-
channel_opening_fee = chan.opening_fee['channel_opening_fee']
2341+
if chan.opening_fee_tlv:
2342+
channel_opening_fee = chan.opening_fee_tlv['channel_opening_fee']
23382343
total_msat -= channel_opening_fee
23392344
amt_to_forward -= channel_opening_fee
23402345
else:
@@ -2388,7 +2393,7 @@ def maybe_fulfill_htlc(
23882393
Return (preimage, (payment_key, callback)) with at most a single element not None.
23892394
"""
23902395
if not processed_onion.are_we_final:
2391-
if not self.lnworker.enable_htlc_forwarding:
2396+
if not self.lnworker.enable_htlc_forwarding or already_forwarded:
23922397
return None, None
23932398
# use the htlc key if we are forwarding
23942399
payment_key = serialize_htlc_key(chan.get_scid_or_local_alias(), htlc.htlc_id)
@@ -2475,7 +2480,7 @@ def log_fail_reason(reason: str):
24752480
log_fail_reason(f"no payment_info found for RHASH {htlc.payment_hash.hex()}")
24762481
raise exc_incorrect_or_unknown_pd
24772482

2478-
preimage = self.lnworker.get_preimage(payment_hash)
2483+
preimage = self.lnworker.get_preimage(payment_hash, only_settleable=True)
24792484
expected_payment_secrets = [self.lnworker.get_payment_secret(htlc.payment_hash)]
24802485
if preimage:
24812486
expected_payment_secrets.append(derive_payment_secret_from_payment_preimage(preimage)) # legacy secret for old invoices
@@ -2502,10 +2507,7 @@ def log_fail_reason(reason: str):
25022507
else:
25032508
return None, None
25042509

2505-
if payment_hash.hex() in self.lnworker.dont_settle_htlcs:
2506-
return None, None
2507-
2508-
chan.opening_fee = None
2510+
chan.opening_fee_tlv = None
25092511
self.logger.info(f"maybe_fulfill_htlc. will FULFILL HTLC: chan {chan.short_channel_id}. htlc={str(htlc)}")
25102512
return preimage, None
25112513

@@ -3093,7 +3095,7 @@ async def wrapped_callback():
30933095
# HTLC we are supposed to forward, and have already forwarded
30943096
# for final trampoline onions, forwarding failures are stored with forwarding_key (which is the inner key)
30953097
payment_key = forwarding_key
3096-
preimage = self.lnworker.get_preimage(payment_hash)
3098+
preimage = self.lnworker.get_preimage(payment_hash, only_settleable=True)
30973099
error_bytes, error_reason = self.lnworker.get_forwarding_failure(payment_key)
30983100
if error_bytes:
30993101
return None, None, error_bytes

electrum/lnwatcher.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def add_channel(self, outpoint: str, address: str) -> None:
6565
cb = lambda: self.check_onchain_situation(address, outpoint)
6666
self.add_callback(address, cb)
6767

68-
async def unwatch_channel(self, address, funding_outpoint):
68+
def unwatch_channel(self, address, funding_outpoint):
6969
self.logger.info(f'unwatching {funding_outpoint}')
7070
self.remove_callback(address)
7171

@@ -128,7 +128,7 @@ async def check_onchain_situation(self, address, funding_outpoint):
128128
closing_height=closing_height,
129129
keep_watching=keep_watching)
130130
if not keep_watching:
131-
await self.unwatch_channel(address, funding_outpoint)
131+
self.unwatch_channel(address, funding_outpoint)
132132

133133
async def sweep_commitment_transaction(self, funding_outpoint: str, closing_tx: Transaction) -> bool:
134134
raise NotImplementedError() # implemented by subclasses

0 commit comments

Comments
 (0)