LN cooperative close: avoid address-reuse (#6590)
Previously if we coop-closed multiple channels in the same session, they would reuse the wallet address.
This commit is contained in:
@@ -145,7 +145,7 @@ class AbstractChannel(Logger, ABC):
|
|||||||
config: Dict[HTLCOwner, Union[LocalConfig, RemoteConfig]]
|
config: Dict[HTLCOwner, Union[LocalConfig, RemoteConfig]]
|
||||||
_sweep_info: Dict[str, Dict[str, 'SweepInfo']]
|
_sweep_info: Dict[str, Dict[str, 'SweepInfo']]
|
||||||
lnworker: Optional['LNWallet']
|
lnworker: Optional['LNWallet']
|
||||||
sweep_address: str
|
_fallback_sweep_address: str
|
||||||
channel_id: bytes
|
channel_id: bytes
|
||||||
funding_outpoint: Outpoint
|
funding_outpoint: Outpoint
|
||||||
node_id: bytes
|
node_id: bytes
|
||||||
@@ -307,6 +307,20 @@ class AbstractChannel(Logger, ABC):
|
|||||||
if self.get_state() == ChannelState.CLOSED and not keep_watching:
|
if self.get_state() == ChannelState.CLOSED and not keep_watching:
|
||||||
self.set_state(ChannelState.REDEEMED)
|
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
|
@abstractmethod
|
||||||
def is_initiator(self) -> bool:
|
def is_initiator(self) -> bool:
|
||||||
pass
|
pass
|
||||||
@@ -367,6 +381,10 @@ class AbstractChannel(Logger, ABC):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def is_static_remotekey_enabled(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ChannelBackup(AbstractChannel):
|
class ChannelBackup(AbstractChannel):
|
||||||
"""
|
"""
|
||||||
@@ -383,7 +401,7 @@ class ChannelBackup(AbstractChannel):
|
|||||||
Logger.__init__(self)
|
Logger.__init__(self)
|
||||||
self.cb = cb
|
self.cb = cb
|
||||||
self._sweep_info = {}
|
self._sweep_info = {}
|
||||||
self.sweep_address = sweep_address
|
self._fallback_sweep_address = sweep_address
|
||||||
self.storage = {} # dummy storage
|
self.storage = {} # dummy storage
|
||||||
self._state = ChannelState.OPENING
|
self._state = ChannelState.OPENING
|
||||||
self.config = {}
|
self.config = {}
|
||||||
@@ -485,7 +503,7 @@ class Channel(AbstractChannel):
|
|||||||
self.name = name
|
self.name = name
|
||||||
Logger.__init__(self)
|
Logger.__init__(self)
|
||||||
self.lnworker = lnworker
|
self.lnworker = lnworker
|
||||||
self.sweep_address = sweep_address
|
self._fallback_sweep_address = sweep_address
|
||||||
self.storage = state
|
self.storage = state
|
||||||
self.db_lock = self.storage.db.lock if self.storage.db else threading.RLock()
|
self.db_lock = self.storage.db.lock if self.storage.db else threading.RLock()
|
||||||
self.config = {}
|
self.config = {}
|
||||||
|
|||||||
@@ -494,7 +494,8 @@ class LNWallet(LNWorker):
|
|||||||
self.features |= LnFeatures.OPTION_STATIC_REMOTEKEY_REQ
|
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.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.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.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
|
self.is_routing = set() # (not persisted) keys of invoices that are in PR_ROUTING state
|
||||||
# used in tests
|
# used in tests
|
||||||
|
|||||||
@@ -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
|
alice.config[REMOTE].next_per_commitment_point = bob_second
|
||||||
bob.config[REMOTE].next_per_commitment_point = alice_second
|
bob.config[REMOTE].next_per_commitment_point = alice_second
|
||||||
|
|
||||||
# TODO: sweep_address in lnchannel.py should use static_remotekey
|
alice._fallback_sweep_address = bitcoin.pubkey_to_address('p2wpkh', alice.config[LOCAL].payment_basepoint.pubkey.hex())
|
||||||
alice.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())
|
||||||
bob.sweep_address = bitcoin.pubkey_to_address('p2wpkh', bob.config[LOCAL].payment_basepoint.pubkey.hex())
|
|
||||||
|
|
||||||
alice._ignore_max_htlc_value = True
|
alice._ignore_max_htlc_value = True
|
||||||
bob._ignore_max_htlc_value = True
|
bob._ignore_max_htlc_value = True
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ class MockWallet:
|
|||||||
def is_lightning_backup(self):
|
def is_lightning_backup(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def is_mine(self, addr):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
|
class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
|
||||||
def __init__(self, *, local_keypair: Keypair, chans: Iterable['Channel'], tx_queue):
|
def __init__(self, *, local_keypair: Keypair, chans: Iterable['Channel'], tx_queue):
|
||||||
|
|||||||
Reference in New Issue
Block a user