commit e6bd3959e08c57ec5eea31e92ac12a4a6f42c3c3
parent 0a08ccc1c6ff910661059a47fa79b1462a927512
Author: Janus <ysangkok@gmail.com>
Date: Mon, 28 Jan 2019 20:13:09 +0100
ln: handle channel limits better, show remote limits in details dialog, replace rusty's testnet peer (doesn't work currently)
Diffstat:
5 files changed, 50 insertions(+), 10 deletions(-)
diff --git a/electrum/gui/qt/channel_details.py b/electrum/gui/qt/channel_details.py
@@ -157,6 +157,16 @@ class ChannelDetailsDialog(QtWidgets.QDialog):
form_layout.addRow(_('Received (mSAT):'), self.received_label)
self.sent_label = SelectableLabel()
form_layout.addRow(_('Sent (mSAT):'), self.sent_label)
+ self.htlc_minimum_msat = SelectableLabel(str(chan.config[REMOTE].htlc_minimum_msat))
+ form_layout.addRow(_('Minimum HTLC value accepted by peer (mSAT):'), self.htlc_minimum_msat)
+ self.max_htlcs = SelectableLabel(str(chan.config[REMOTE].max_accepted_htlcs))
+ form_layout.addRow(_('Maximum number of concurrent HTLCs accepted by peer:'), self.max_htlcs)
+ self.max_htlc_value = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].max_htlc_value_in_flight_msat / 1000))
+ form_layout.addRow(_('Maximum value of in-flight HTLCs accepted by peer:'), self.max_htlc_value)
+ self.dust_limit = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].dust_limit_sat))
+ form_layout.addRow(_('Remote dust limit:'), self.dust_limit)
+ self.reserve = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].reserve_sat))
+ form_layout.addRow(_('Remote channel reserve:'), self.reserve)
# add htlc tree view to vbox (wouldn't scale correctly in QFormLayout)
form_layout.addRow(_('Payments (HTLCs):'), None)
diff --git a/electrum/lnbase.py b/electrum/lnbase.py
@@ -33,7 +33,9 @@ from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc,
secret_to_pubkey, LNPeerAddr, PaymentFailure, LnLocalFeatures,
LOCAL, REMOTE, HTLCOwner, generate_keypair, LnKeyFamily,
get_ln_flag_pair_of_bit, privkey_to_pubkey, UnknownPaymentHash, MIN_FINAL_CLTV_EXPIRY_ACCEPTED,
- LightningPeerConnectionClosed, HandshakeFailed, LNPeerAddr, NotFoundChanAnnouncementForUpdate)
+ LightningPeerConnectionClosed, HandshakeFailed, LNPeerAddr, NotFoundChanAnnouncementForUpdate,
+ MINIMUM_MAX_HTLC_VALUE_IN_FLIGHT_ACCEPTED, MAXIMUM_HTLC_MINIMUM_MSAT_ACCEPTED,
+ MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED)
from .lntransport import LNTransport, LNTransportBase
if TYPE_CHECKING:
@@ -400,7 +402,7 @@ class Peer(PrintError):
revocation_basepoint=keypair_generator(LnKeyFamily.REVOCATION_BASE),
to_self_delay=9,
dust_limit_sat=546,
- max_htlc_value_in_flight_msat=0xffffffffffffffff,
+ max_htlc_value_in_flight_msat=200000000,
max_accepted_htlcs=5,
initial_msat=initial_msat,
ctn=-1,
@@ -418,6 +420,7 @@ class Peer(PrintError):
@log_exceptions
async def channel_establishment_flow(self, password: Optional[str], funding_sat: int,
push_msat: int, temp_channel_id: bytes) -> Channel:
+ assert push_msat == 0, "push_msat not supported currently"
wallet = self.lnworker.wallet
# dry run creating funding tx to see if we even have enough funds
funding_tx_test = wallet.mktx([TxOutput(bitcoin.TYPE_ADDRESS, wallet.dummy_address(), funding_sat)],
@@ -448,33 +451,49 @@ class Peer(PrintError):
max_htlc_value_in_flight_msat=local_config.max_htlc_value_in_flight_msat,
channel_flags=0x00, # not willing to announce channel
channel_reserve_satoshis=local_config.reserve_sat,
+ htlc_minimum_msat=1,
)
payload = await self.channel_accepted[temp_channel_id].get()
if payload.get('error'):
raise Exception('Remote Lightning peer reported error: ' + repr(payload.get('error')))
remote_per_commitment_point = payload['first_per_commitment_point']
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
+ assert funding_txn_minimum_depth > 0, funding_txn_minimum_depth
remote_dust_limit_sat = int.from_bytes(payload['dust_limit_satoshis'], byteorder='big')
- assert remote_dust_limit_sat < 600, remote_dust_limit_sat
- assert int.from_bytes(payload['htlc_minimum_msat'], 'big') < 600 * 1000
+ remote_reserve_sat = self.validate_remote_reserve(payload["channel_reserve_satoshis"], remote_dust_limit_sat, funding_sat)
+ if remote_dust_limit_sat > remote_reserve_sat:
+ raise Exception(f"Remote Lightning peer reports dust_limit_sat > reserve_sat which is a BOLT-02 protocol violation.")
+ htlc_min = int.from_bytes(payload['htlc_minimum_msat'], 'big')
+ if htlc_min > MAXIMUM_HTLC_MINIMUM_MSAT_ACCEPTED:
+ raise Exception(f"Remote Lightning peer reports htlc_minimum_msat={htlc_min} mSAT," +
+ f" which is above Electrums required maximum limit of that parameter ({MAXIMUM_HTLC_MINIMUM_MSAT_ACCEPTED} mSAT).")
remote_max = int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big')
- assert remote_max >= 198 * 1000 * 1000, remote_max
+ if remote_max < MINIMUM_MAX_HTLC_VALUE_IN_FLIGHT_ACCEPTED:
+ raise Exception(f"Remote Lightning peer reports max_htlc_value_in_flight_msat at only {remote_max} mSAT" +
+ f" which is below Electrums required minimum ({MINIMUM_MAX_HTLC_VALUE_IN_FLIGHT_ACCEPTED} mSAT).")
+ max_accepted_htlcs = int.from_bytes(payload["max_accepted_htlcs"], 'big')
+ if max_accepted_htlcs > 483:
+ raise Exception("Remote Lightning peer reports max_accepted_htlcs > 483, which is a BOLT-02 protocol violation.")
+ remote_to_self_delay = int.from_bytes(payload['to_self_delay'], byteorder='big')
+ if remote_to_self_delay > MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED:
+ raise Exception(f"Remote Lightning peer reports to_self_delay={remote_to_self_delay}," +
+ f" which is above Electrums required maximum ({MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED})")
their_revocation_store = RevocationStore()
- remote_reserve_sat = self.validate_remote_reserve(payload["channel_reserve_satoshis"], remote_dust_limit_sat, funding_sat)
remote_config = RemoteConfig(
payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
multisig_key=OnlyPubkeyKeypair(payload["funding_pubkey"]),
htlc_basepoint=OnlyPubkeyKeypair(payload['htlc_basepoint']),
delayed_basepoint=OnlyPubkeyKeypair(payload['delayed_payment_basepoint']),
revocation_basepoint=OnlyPubkeyKeypair(payload['revocation_basepoint']),
- to_self_delay=int.from_bytes(payload['to_self_delay'], byteorder='big'),
+ to_self_delay=remote_to_self_delay,
dust_limit_sat=remote_dust_limit_sat,
max_htlc_value_in_flight_msat=remote_max,
- max_accepted_htlcs=int.from_bytes(payload["max_accepted_htlcs"], 'big'),
+ max_accepted_htlcs=max_accepted_htlcs,
initial_msat=push_msat,
ctn = -1,
next_htlc_id = 0,
reserve_sat = remote_reserve_sat,
+ htlc_minimum_msat = htlc_min,
next_per_commitment_point=remote_per_commitment_point,
current_per_commitment_point=None,
@@ -531,6 +550,7 @@ class Peer(PrintError):
raise Exception('wrong chain_hash')
funding_sat = int.from_bytes(payload['funding_satoshis'], 'big')
push_msat = int.from_bytes(payload['push_msat'], 'big')
+ assert push_msat == 0, "push_msat not supported currently"
feerate = int.from_bytes(payload['feerate_per_kw'], 'big')
temp_chan_id = payload['temporary_channel_id']
diff --git a/electrum/lnchan.py b/electrum/lnchan.py
@@ -206,7 +206,7 @@ class Channel(PrintError):
current_htlc_sum = htlcsum(self.hm.htlcs_by_direction(LOCAL, SENT)) + htlcsum(self.hm.htlcs_by_direction(LOCAL, RECEIVED))
if current_htlc_sum + amount_msat > self.config[REMOTE].max_htlc_value_in_flight_msat:
raise PaymentFailure(f'HTLC value sum (sum of pending htlcs: {current_htlc_sum/1000} sat plus new htlc: {amount_msat/1000} sat) would exceed max allowed: {self.config[REMOTE].max_htlc_value_in_flight_msat/1000} sat')
- if amount_msat <= 0: # FIXME htlc_minimum_msat
+ if amount_msat < self.config[REMOTE].htlc_minimum_msat:
raise PaymentFailure(f'HTLC value too small: {amount_msat} msat')
def can_pay(self, amount_msat):
diff --git a/electrum/lnutil.py b/electrum/lnutil.py
@@ -71,6 +71,7 @@ class RemoteConfig(NamedTuple):
max_accepted_htlcs: int
initial_msat: int
reserve_sat: int
+ htlc_minimum_msat: int
# specific to "REMOTE" config
next_per_commitment_point: bytes
revocation_store: 'RevocationStore'
@@ -103,6 +104,15 @@ MIN_FINAL_CLTV_EXPIRY_ACCEPTED = 144
MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE = MIN_FINAL_CLTV_EXPIRY_ACCEPTED + 1
+# When we open a channel, the remote peer has to support at least this
+# value of mSATs in HTLCs accumulated on the channel, or we refuse opening.
+# Number is based on observed testnet limit https://github.com/spesmilo/electrum/issues/5032
+MINIMUM_MAX_HTLC_VALUE_IN_FLIGHT_ACCEPTED = 19_800 * 1000
+
+MAXIMUM_HTLC_MINIMUM_MSAT_ACCEPTED = 1000
+
+MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED = 2016
+
class RevocationStore:
""" Taken from LND, see license in lnchan.py. """
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
@@ -53,7 +53,7 @@ GRAPH_DOWNLOAD_SECONDS = 600
FALLBACK_NODE_LIST_TESTNET = (
LNPeerAddr('ecdsa.net', 9735, bfh('038370f0e7a03eded3e1d41dc081084a87f0afa1c5b22090b4f3abb391eb15d8ff')),
- LNPeerAddr('165.227.30.200', 9735, bfh('023ea0a53af875580899da0ab0a21455d9c19160c4ea1b7774c9d4be6810b02d2c')),
+ LNPeerAddr('180.181.208.42', 9735, bfh('038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9')),
)
FALLBACK_NODE_LIST_MAINNET = (
LNPeerAddr('104.198.32.198', 9735, bfh('02f6725f9c1c40333b67faea92fd211c183050f28df32cac3f9d69685fe9665432')), # Blockstream