electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 3eba26b3980719647d826dfb664e39fd51ef0893
parent fc89c8ffa9b1ab4012331447090b36f6f15b13fc
Author: ghost43 <somber.night@protonmail.com>
Date:   Tue, 15 Sep 2020 15:37:47 +0000

LN cooperative close: avoid address-reuse (#6590)

Previously if we coop-closed multiple channels in the same session,
they would reuse the wallet address.
Diffstat:
Melectrum/lnchannel.py | 24+++++++++++++++++++++---
Melectrum/lnworker.py | 3++-
Melectrum/tests/test_lnchannel.py | 5++---
Melectrum/tests/test_lnpeer.py | 3+++
4 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py @@ -145,7 +145,7 @@ class AbstractChannel(Logger, ABC): config: Dict[HTLCOwner, Union[LocalConfig, RemoteConfig]] _sweep_info: Dict[str, Dict[str, 'SweepInfo']] lnworker: Optional['LNWallet'] - sweep_address: str + _fallback_sweep_address: str channel_id: bytes funding_outpoint: Outpoint node_id: bytes @@ -307,6 +307,20 @@ class AbstractChannel(Logger, ABC): if self.get_state() == ChannelState.CLOSED and not keep_watching: self.set_state(ChannelState.REDEEMED) + @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: pass @@ -367,6 +381,10 @@ class AbstractChannel(Logger, ABC): """ pass + @abstractmethod + def is_static_remotekey_enabled(self) -> bool: + pass + class ChannelBackup(AbstractChannel): """ @@ -383,7 +401,7 @@ class ChannelBackup(AbstractChannel): Logger.__init__(self) self.cb = cb self._sweep_info = {} - self.sweep_address = sweep_address + self._fallback_sweep_address = sweep_address self.storage = {} # dummy storage self._state = ChannelState.OPENING self.config = {} @@ -485,7 +503,7 @@ class Channel(AbstractChannel): self.name = name Logger.__init__(self) self.lnworker = lnworker - self.sweep_address = sweep_address + 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 = {} diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -494,7 +494,8 @@ class LNWallet(LNWorker): self.features |= LnFeatures.OPTION_STATIC_REMOTEKEY_REQ self.payments = self.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid # FIXME amt should be msat self.preimages = self.db.get_dict('lightning_preimages') # RHASH -> preimage - self.sweep_address = wallet.get_new_sweep_address_for_channel() # TODO possible address-reuse + # 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[PaymentAttemptLog]] # key is RHASH # (not persisted) self.is_routing = set() # (not persisted) keys of invoices that are in PR_ROUTING state # used in tests diff --git a/electrum/tests/test_lnchannel.py b/electrum/tests/test_lnchannel.py @@ -172,9 +172,8 @@ def create_test_channels(*, feerate=6000, local_msat=None, remote_msat=None, alice.config[REMOTE].next_per_commitment_point = bob_second bob.config[REMOTE].next_per_commitment_point = alice_second - # TODO: sweep_address in lnchannel.py should use static_remotekey - alice.sweep_address = bitcoin.pubkey_to_address('p2wpkh', alice.config[LOCAL].payment_basepoint.pubkey.hex()) - bob.sweep_address = bitcoin.pubkey_to_address('p2wpkh', bob.config[LOCAL].payment_basepoint.pubkey.hex()) + alice._fallback_sweep_address = bitcoin.pubkey_to_address('p2wpkh', alice.config[LOCAL].payment_basepoint.pubkey.hex()) + bob._fallback_sweep_address = bitcoin.pubkey_to_address('p2wpkh', bob.config[LOCAL].payment_basepoint.pubkey.hex()) alice._ignore_max_htlc_value = True bob._ignore_max_htlc_value = True diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py @@ -103,6 +103,9 @@ class MockWallet: def is_lightning_backup(self): return False + def is_mine(self, addr): + return True + class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]): def __init__(self, *, local_keypair: Keypair, chans: Iterable['Channel'], tx_queue):