diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py index 6d1174494..06feb8ba1 100644 --- a/electrum/lnchannel.py +++ b/electrum/lnchannel.py @@ -179,7 +179,6 @@ class AbstractChannel(Logger, ABC): config: Dict[HTLCOwner, Union[LocalConfig, RemoteConfig]] _sweep_info: Dict[str, Dict[str, 'SweepInfo']] lnworker: Optional['LNWallet'] - _fallback_sweep_address: str channel_id: bytes short_channel_id: Optional[ShortChannelID] = None funding_outpoint: Outpoint @@ -355,19 +354,6 @@ class AbstractChannel(Logger, ABC): # auto-remove redeemed backups self.lnworker.remove_channel_backup(self.channel_id) - @property - def sweep_address(self) -> str: - # TODO: in case of unilateral close with pending HTLCs, this address will be reused - addr = None - if self.is_static_remotekey_enabled(): - our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey - addr = make_commitment_output_to_remote_address(our_payment_pubkey) - if addr is None: - addr = self._fallback_sweep_address - assert addr - if self.lnworker: - assert self.lnworker.wallet.is_mine(addr) - return addr @abstractmethod def is_initiator(self) -> bool: @@ -456,13 +442,12 @@ class ChannelBackup(AbstractChannel): - will need to sweep their ctx to_remote """ - def __init__(self, cb: ChannelBackupStorage, *, sweep_address=None, lnworker=None): + def __init__(self, cb: ChannelBackupStorage, *, lnworker=None): self.name = None Logger.__init__(self) self.cb = cb self.is_imported = isinstance(self.cb, ImportedChannelBackupStorage) self._sweep_info = {} - self._fallback_sweep_address = sweep_address self.storage = {} # dummy storage self._state = ChannelState.OPENING self.node_id = cb.node_id if self.is_imported else cb.node_id_prefix @@ -566,10 +551,13 @@ class ChannelBackup(AbstractChannel): return False def is_static_remotekey_enabled(self) -> bool: - # Return False so that self.sweep_address will return self._fallback_sweep_address + return False + + @property + def sweep_address(self) -> str: # Since channel backups do not save the static_remotekey, payment_basepoint in # their local config is not static) - return False + return self.lnworker.wallet.get_new_sweep_address_for_channel() def get_local_pubkey(self) -> bytes: cb = self.cb @@ -600,13 +588,12 @@ class Channel(AbstractChannel): def __repr__(self): return "Channel(%s)"%self.get_id_for_log() - def __init__(self, state: 'StoredDict', *, sweep_address=None, name=None, lnworker=None, initial_feerate=None): + def __init__(self, state: 'StoredDict', *, name=None, lnworker=None, initial_feerate=None): self.name = name self.channel_id = bfh(state["channel_id"]) self.short_channel_id = ShortChannelID.normalize(state["short_channel_id"]) Logger.__init__(self) # should be after short_channel_id is set self.lnworker = lnworker - self._fallback_sweep_address = sweep_address self.storage = state self.db_lock = self.storage.db.lock if self.storage.db else threading.RLock() self.config = {} @@ -763,13 +750,22 @@ class Channel(AbstractChannel): channel_type = ChannelType(self.storage.get('channel_type')) return bool(channel_type & ChannelType.OPTION_STATIC_REMOTEKEY) + @property + def sweep_address(self) -> str: + # TODO: in case of unilateral close with pending HTLCs, this address will be reused + addr = None + assert self.is_static_remotekey_enabled() + our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey + addr = make_commitment_output_to_remote_address(our_payment_pubkey) + if self.lnworker: + assert self.lnworker.wallet.is_mine(addr) + return addr + def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]: - ret = [] - if self.is_static_remotekey_enabled(): - our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey - to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey) - ret.append(to_remote_address) - return ret + assert self.is_static_remotekey_enabled() + our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey + to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey) + return [to_remote_address] def get_feerate(self, subject: HTLCOwner, *, ctn: int) -> int: # returns feerate in sat/kw @@ -1471,12 +1467,8 @@ class Channel(AbstractChannel): feerate=feerate, is_local_initiator=self.constraints.is_initiator == (subject == LOCAL), ) - - if self.is_static_remotekey_enabled(): - payment_pubkey = other_config.payment_basepoint.pubkey - else: - payment_pubkey = derive_pubkey(other_config.payment_basepoint.pubkey, this_point) - + assert self.is_static_remotekey_enabled() + payment_pubkey = other_config.payment_basepoint.pubkey return make_commitment( ctn=ctn, local_funding_pubkey=this_config.multisig_key.pubkey, diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index 882d7d64b..a5ff93716 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -847,7 +847,6 @@ class Peer(Logger): channel_id, outpoint, local_config, remote_config, constraints, our_channel_type) chan = Channel( storage, - sweep_address=self.lnworker.sweep_address, lnworker=self.lnworker, initial_feerate=feerate ) @@ -1026,7 +1025,6 @@ class Peer(Logger): channel_id, outpoint, local_config, remote_config, constraints, channel_type) chan = Channel( chan_dict, - sweep_address=self.lnworker.sweep_address, lnworker=self.lnworker, initial_feerate=feerate ) @@ -1154,18 +1152,7 @@ class Peer(Logger): f"channel_reestablish ({chan.get_id_for_log()}): " f"(DLP) local PCS mismatch: {bh2u(our_pcs)} != {bh2u(their_claim_of_our_last_per_commitment_secret)}") return False - if chan.is_static_remotekey_enabled(): - return True - try: - __, our_remote_pcp = chan.get_secret_and_point(REMOTE, their_next_local_ctn - 1) - except RemoteCtnTooFarInFuture: - pass - else: - if our_remote_pcp != their_local_pcp: - self.logger.error( - f"channel_reestablish ({chan.get_id_for_log()}): " - f"(DLP) remote PCP mismatch: {bh2u(our_remote_pcp)} != {bh2u(their_local_pcp)}") - return False + assert chan.is_static_remotekey_enabled() return True if not are_datalossprotect_fields_valid(): raise RemoteMisbehaving("channel_reestablish: data loss protect fields invalid") @@ -1215,10 +1202,8 @@ class Peer(Logger): # BOLT-02: "A node [...] upon disconnection [...] MUST reverse any uncommitted updates sent by the other side" chan.hm.discard_unsigned_remote_updates() # send message - if chan.is_static_remotekey_enabled(): - latest_secret, latest_point = chan.get_secret_and_point(LOCAL, 0) - else: - latest_secret, latest_point = chan.get_secret_and_point(LOCAL, latest_local_ctn) + assert chan.is_static_remotekey_enabled() + latest_secret, latest_point = chan.get_secret_and_point(LOCAL, 0) if oldest_unrevoked_remote_ctn == 0: last_rev_secret = 0 else: diff --git a/electrum/lnsweep.py b/electrum/lnsweep.py index 06b3ddbb1..5af1d5a61 100644 --- a/electrum/lnsweep.py +++ b/electrum/lnsweep.py @@ -205,8 +205,8 @@ def create_sweeptxs_for_our_ctx( their_revocation_pubkey, to_self_delay, our_localdelayed_pubkey)) to_local_address = redeem_script_to_address('p2wsh', to_local_witness_script) # to remote address - bpk = their_conf.payment_basepoint.pubkey - their_payment_pubkey = bpk if chan.is_static_remotekey_enabled() else derive_pubkey(their_conf.payment_basepoint.pubkey, our_pcp) + assert chan.is_static_remotekey_enabled() + their_payment_pubkey = their_conf.payment_basepoint.pubkey to_remote_address = make_commitment_output_to_remote_address(their_payment_pubkey) # test ctx _logger.debug(f'testing our ctx: {to_local_address} {to_remote_address}') @@ -356,8 +356,8 @@ def create_sweeptxs_for_their_ctx( our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey)) to_local_address = redeem_script_to_address('p2wsh', witness_script) # to remote address - bpk = our_conf.payment_basepoint.pubkey - our_payment_pubkey = bpk if chan.is_static_remotekey_enabled() else derive_pubkey(bpk, their_pcp) + assert chan.is_static_remotekey_enabled() + our_payment_pubkey = our_conf.payment_basepoint.pubkey to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey) # test if this is their ctx _logger.debug(f'testing their ctx: {to_local_address} {to_remote_address}') @@ -379,27 +379,6 @@ def create_sweeptxs_for_their_ctx( our_htlc_privkey = ecc.ECPrivkey.from_secret_scalar(our_htlc_privkey) their_htlc_pubkey = derive_pubkey(their_conf.htlc_basepoint.pubkey, their_pcp) # to_local is handled by lnwatcher - # to_remote - if not chan.is_static_remotekey_enabled(): - our_payment_bp_privkey = ecc.ECPrivkey(our_conf.payment_basepoint.privkey) - our_payment_privkey = derive_privkey(our_payment_bp_privkey.secret_scalar, their_pcp) - our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey) - assert our_payment_pubkey == our_payment_privkey.get_public_key_bytes(compressed=True) - output_idxs = ctx.get_output_idxs_from_address(to_remote_address) - if output_idxs: - output_idx = output_idxs.pop() - prevout = ctx.txid() + ':%d'%output_idx - sweep_tx = lambda: create_sweeptx_their_ctx_to_remote( - sweep_address=sweep_address, - ctx=ctx, - output_idx=output_idx, - our_payment_privkey=our_payment_privkey, - config=chan.lnworker.config) - txs[prevout] = SweepInfo( - name='their_ctx_to_remote', - csv_delay=0, - cltv_expiry=0, - gen_tx=sweep_tx) # HTLCs def create_sweeptx_for_htlc( *, htlc: 'UpdateAddHtlc', diff --git a/electrum/lnworker.py b/electrum/lnworker.py index b16878eaf..07bf038f2 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -630,7 +630,6 @@ class LNWallet(LNWorker): self.payment_info = self.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid self.preimages = self.db.get_dict('lightning_preimages') # RHASH -> preimage # note: this sweep_address is only used as fallback; as it might result in address-reuse - self.sweep_address = wallet.get_new_sweep_address_for_channel() self.logs = defaultdict(list) # type: Dict[str, List[HtlcLog]] # key is RHASH # (not persisted) # used in tests self.enable_htlc_settle = True @@ -640,14 +639,14 @@ class LNWallet(LNWorker): self._channels = {} # type: Dict[bytes, Channel] channels = self.db.get_dict("channels") for channel_id, c in random_shuffled_copy(channels.items()): - self._channels[bfh(channel_id)] = Channel(c, sweep_address=self.sweep_address, lnworker=self) + self._channels[bfh(channel_id)] = Channel(c, lnworker=self) self._channel_backups = {} # type: Dict[bytes, ChannelBackup] # order is important: imported should overwrite onchain for name in ["onchain_channel_backups", "imported_channel_backups"]: channel_backups = self.db.get_dict(name) for channel_id, storage in channel_backups.items(): - self._channel_backups[bfh(channel_id)] = ChannelBackup(storage, sweep_address=self.sweep_address, lnworker=self) + self._channel_backups[bfh(channel_id)] = ChannelBackup(storage, lnworker=self) self.sent_htlcs = defaultdict(asyncio.Queue) # type: Dict[bytes, asyncio.Queue[HtlcLog]] self.sent_htlcs_info = dict() # (RHASH, scid, htlc_id) -> route, payment_secret, amount_msat, bucket_msat, trampoline_fee_level @@ -2502,7 +2501,7 @@ class LNWallet(LNWorker): d = self.db.get_dict("imported_channel_backups") d[channel_id.hex()] = cb_storage with self.lock: - cb = ChannelBackup(cb_storage, sweep_address=self.sweep_address, lnworker=self) + cb = ChannelBackup(cb_storage, lnworker=self) self._channel_backups[channel_id] = cb self.wallet.save_db() util.trigger_callback('channels_updated', self.wallet) @@ -2616,7 +2615,7 @@ class LNWallet(LNWorker): self.logger.info(f"adding backup from tx") d = self.db.get_dict("onchain_channel_backups") d[channel_id] = cb_storage - cb = ChannelBackup(cb_storage, sweep_address=self.sweep_address, lnworker=self) + cb = ChannelBackup(cb_storage, lnworker=self) self.wallet.save_db() with self.lock: self._channel_backups[bfh(channel_id)] = cb