option_zeroconf
- accept zeroconf channels only from a single node - fw_info uses get_scid_or_local_alias
This commit is contained in:
@@ -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"),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()})")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user