Browse Source

option_zeroconf

- accept zeroconf channels only from a single node
 - fw_info uses get_scid_or_local_alias
master
ThomasV 2 years ago
parent
commit
816e617aaf
  1. 4
      electrum/commands.py
  2. 9
      electrum/lnchannel.py
  3. 23
      electrum/lnpeer.py
  4. 7
      electrum/lnutil.py
  5. 13
      electrum/lnworker.py
  6. 4
      electrum/simple_config.py

4
electrum/commands.py

@ -1137,7 +1137,7 @@ class Commands:
} for p in lnworker.peers.values()]
@command('wpnl')
async def open_channel(self, connection_string, amount, push_amount=0, public=False, password=None, wallet: Abstract_Wallet = None):
async def open_channel(self, connection_string, amount, push_amount=0, public=False, zeroconf=False, password=None, wallet: Abstract_Wallet = None):
funding_sat = satoshis(amount)
push_sat = satoshis(push_amount)
peer = await wallet.lnworker.add_peer(connection_string)
@ -1145,6 +1145,7 @@ class Commands:
peer, funding_sat,
push_sat=push_sat,
public=public,
zeroconf=zeroconf,
password=password)
return chan.funding_outpoint.to_str()
@ -1449,6 +1450,7 @@ command_options = {
'force': (None, "Create new address beyond gap limit, if no more addresses are available."),
'pending': (None, "Show only pending requests."),
'push_amount': (None, 'Push initial amount (in BTC)'),
'zeroconf': (None, 'request zeroconf channel'),
'expired': (None, "Show only expired requests."),
'paid': (None, "Show only paid requests."),
'show_addresses': (None, "Show input and output addresses"),

9
electrum/lnchannel.py

@ -695,6 +695,9 @@ class Channel(AbstractChannel):
alias = self.storage.get('alias')
return bytes.fromhex(alias) if alias else None
def get_scid_or_local_alias(self):
return self.short_channel_id or self.get_local_scid_alias()
def has_onchain_backup(self):
return self.storage.get('has_onchain_backup', False)
@ -831,6 +834,10 @@ class Channel(AbstractChannel):
channel_type = ChannelType(self.storage.get('channel_type'))
return bool(channel_type & ChannelType.OPTION_STATIC_REMOTEKEY)
def is_zeroconf(self) -> bool:
channel_type = ChannelType(self.storage.get('channel_type'))
return bool(channel_type & ChannelType.OPTION_ZEROCONF)
@property
def sweep_address(self) -> str:
# TODO: in case of unilateral close with pending HTLCs, this address will be reused
@ -1696,7 +1703,7 @@ class Channel(AbstractChannel):
if conf < self.funding_txn_minimum_depth():
#self.logger.info(f"funding tx is still not at sufficient depth. actual depth: {conf}")
return False
assert conf > 0
assert conf > 0 or self.is_zeroconf()
# check funding_tx amount and script
funding_tx = self.lnworker.lnwatcher.adb.get_transaction(funding_txid)
if not funding_tx:

23
electrum/lnpeer.py

@ -627,6 +627,9 @@ class Peer(Logger):
def is_channel_type(self):
return self.features.supports(LnFeatures.OPTION_CHANNEL_TYPE_OPT)
def accepts_zeroconf(self):
return self.features.supports(LnFeatures.OPTION_ZEROCONF_OPT)
def is_upfront_shutdown_script(self):
return self.features.supports(LnFeatures.OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT)
@ -710,6 +713,7 @@ class Peer(Logger):
funding_sat: int,
push_msat: int,
public: bool,
zeroconf: bool = False,
temp_channel_id: bytes
) -> Tuple[Channel, 'PartialTransaction']:
"""Implements the channel opening flow.
@ -736,6 +740,8 @@ class Peer(Logger):
open_channel_tlvs = {}
assert self.their_features.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT)
our_channel_type = ChannelType(ChannelType.OPTION_STATIC_REMOTEKEY)
if zeroconf:
our_channel_type |= ChannelType(ChannelType.OPTION_ZEROCONF)
# We do not set the option_scid_alias bit in channel_type because LND rejects it.
# Eclair accepts channel_type with that bit, but does not require it.
@ -791,7 +797,7 @@ class Peer(Logger):
self.logger.debug(f"received accept_channel for temp_channel_id={temp_channel_id.hex()}. {payload=}")
remote_per_commitment_point = payload['first_per_commitment_point']
funding_txn_minimum_depth = payload['minimum_depth']
if funding_txn_minimum_depth <= 0:
if not zeroconf and funding_txn_minimum_depth <= 0:
raise Exception(f"minimum depth too low, {funding_txn_minimum_depth}")
if funding_txn_minimum_depth > 30:
raise Exception(f"minimum depth too high, {funding_txn_minimum_depth}")
@ -903,6 +909,9 @@ class Peer(Logger):
await self.send_warning(channel_id, message=str(e), close_connection=True)
chan.open_with_first_pcp(remote_per_commitment_point, remote_sig)
chan.set_state(ChannelState.OPENING)
if zeroconf:
chan.set_state(ChannelState.FUNDED)
self.send_channel_ready(chan)
self.lnworker.add_new_channel(chan)
return chan, funding_tx
@ -1009,7 +1018,12 @@ class Peer(Logger):
)
per_commitment_point_first = secret_to_pubkey(
int.from_bytes(per_commitment_secret_first, 'big'))
min_depth = 3
is_zeroconf = channel_type & channel_type.OPTION_ZEROCONF
if is_zeroconf and not self.network.config.ZEROCONF_TRUSTED_NODE.startswith(self.pubkey.hex()):
raise Exception(f"not accepting zeroconf from node {self.pubkey}")
min_depth = 0 if is_zeroconf else 3
accept_channel_tlvs = {
'upfront_shutdown_script': {
'shutdown_scriptpubkey': local_config.upfront_shutdown_script
@ -1078,6 +1092,9 @@ class Peer(Logger):
self.funding_signed_sent.add(chan.channel_id)
chan.open_with_first_pcp(payload['first_per_commitment_point'], remote_sig)
chan.set_state(ChannelState.OPENING)
if is_zeroconf:
chan.set_state(ChannelState.FUNDED)
self.send_channel_ready(chan)
self.lnworker.add_new_channel(chan)
async def request_force_close(self, channel_id: bytes):
@ -1421,7 +1438,7 @@ class Peer(Logger):
chan.set_remote_update(pending_channel_update)
self.logger.info(f"CHANNEL OPENING COMPLETED ({chan.get_id_for_log()})")
forwarding_enabled = self.network.config.EXPERIMENTAL_LN_FORWARD_PAYMENTS
if forwarding_enabled:
if forwarding_enabled and chan.short_channel_id:
# send channel_update of outgoing edge to peer,
# so that channel can be used to to receive payments
self.logger.info(f"sending channel update for outgoing edge ({chan.get_id_for_log()})")

7
electrum/lnutil.py

@ -1206,6 +1206,13 @@ class LnFeatures(IntFlag):
_ln_feature_contexts[OPTION_SCID_ALIAS_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
_ln_feature_contexts[OPTION_SCID_ALIAS_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
OPTION_ZEROCONF_REQ = 1 << 50
OPTION_ZEROCONF_OPT = 1 << 51
_ln_feature_direct_dependencies[OPTION_ZEROCONF_OPT] = {OPTION_SCID_ALIAS_OPT}
_ln_feature_contexts[OPTION_ZEROCONF_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
_ln_feature_contexts[OPTION_ZEROCONF_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
def validate_transitive_dependencies(self) -> bool:
# for all even bit set, set corresponding odd bit:
features = self # copy

13
electrum/lnworker.py

@ -798,13 +798,16 @@ class LNWallet(LNWorker):
def __init__(self, wallet: 'Abstract_Wallet', xprv):
self.wallet = wallet
self.config = wallet.config
self.db = wallet.db
self.node_keypair = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.NODE_KEY)
self.backup_key = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.BACKUP_CIPHER).privkey
self.payment_secret_key = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.PAYMENT_SECRET_KEY).privkey
Logger.__init__(self)
LNWorker.__init__(self, self.node_keypair, LNWALLET_FEATURES)
self.config = wallet.config
features = LNWALLET_FEATURES
if self.config.ACCEPT_ZEROCONF_CHANNELS:
features |= LnFeatures.OPTION_ZEROCONF_OPT
LNWorker.__init__(self, self.node_keypair, features)
self.lnwatcher = None
self.lnrater: LNRater = None
self.payment_info = self.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid
@ -1223,6 +1226,7 @@ class LNWallet(LNWorker):
self, peer, funding_sat, *,
push_sat: int = 0,
public: bool = False,
zeroconf: bool = False,
password=None):
coins = self.wallet.get_spendable_coins(None)
node_id = peer.pubkey
@ -1237,6 +1241,7 @@ class LNWallet(LNWorker):
funding_sat=funding_sat,
push_sat=push_sat,
public=public,
zeroconf=zeroconf,
password=password)
return chan, funding_tx
@ -1248,6 +1253,7 @@ class LNWallet(LNWorker):
funding_sat: int,
push_sat: int,
public: bool,
zeroconf=False,
password: Optional[str]) -> Tuple[Channel, PartialTransaction]:
coro = peer.channel_establishment_flow(
@ -1255,6 +1261,7 @@ class LNWallet(LNWorker):
funding_sat=funding_sat,
push_msat=push_sat * 1000,
public=public,
zeroconf=zeroconf,
temp_channel_id=os.urandom(32))
chan, funding_tx = await util.wait_for2(coro, LN_P2P_NETWORK_TIMEOUT)
util.trigger_callback('channels_updated', self.wallet)
@ -2306,7 +2313,7 @@ class LNWallet(LNWorker):
If we find this was a forwarded HTLC, the upstream peer is notified.
Returns whether this was a forwarded HTLC.
"""
fw_info = chan.short_channel_id.hex(), htlc_id
fw_info = chan.get_scid_or_local_alias().hex(), htlc_id
upstream_peer_pubkey = self.downstream_htlc_to_upstream_peer_map.get(fw_info)
if not upstream_peer_pubkey:
return False

4
electrum/simple_config.py

@ -1169,6 +1169,10 @@ This will result in longer routes; it might increase your fees and decrease the
SWAPSERVER_PORT = ConfigVar('swapserver_port', default=5455, type_=int)
TEST_SWAPSERVER_REFUND = ConfigVar('test_swapserver_refund', default=False, type_=bool)
# zeroconf
ACCEPT_ZEROCONF_CHANNELS = ConfigVar('accept_zeroconf_channels', default=False, type_=bool)
ZEROCONF_TRUSTED_NODE = ConfigVar('zeroconf_trusted_node', default='', type_=str)
# connect to remote WT
WATCHTOWER_CLIENT_ENABLED = ConfigVar(
'use_watchtower', default=False, type_=bool,

Loading…
Cancel
Save