Merge pull request #9265 from SomberNight/202410_ln_address_reuse_2
lnworker: reserve wallet addresses also for chan backups
This commit is contained in:
@@ -488,6 +488,14 @@ class AbstractChannel(Logger, ABC):
|
|||||||
def can_be_deleted(self) -> bool:
|
def can_be_deleted(self) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]:
|
||||||
|
"""Returns a list of addrs that the wallet should not use, to avoid address-reuse.
|
||||||
|
Typically, these addresses are wallet.is_mine, but that is not guaranteed,
|
||||||
|
in which case the wallet can just ignore those.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ChannelBackup(AbstractChannel):
|
class ChannelBackup(AbstractChannel):
|
||||||
"""
|
"""
|
||||||
@@ -639,6 +647,19 @@ class ChannelBackup(AbstractChannel):
|
|||||||
ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
|
ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]:
|
||||||
|
if self.is_imported:
|
||||||
|
# For v1 imported cbs, we have the local_payment_pubkey, which is
|
||||||
|
# directly used as p2wpkh() of static_remotekey channels.
|
||||||
|
# (for v0 imported cbs, the correct local_payment_pubkey is missing, and so
|
||||||
|
# we might calculate a different address here, which might not be wallet.is_mine,
|
||||||
|
# but that should be harmless)
|
||||||
|
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]
|
||||||
|
else: # on-chain backup
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class Channel(AbstractChannel):
|
class Channel(AbstractChannel):
|
||||||
# note: try to avoid naming ctns/ctxs/etc as "current" and "pending".
|
# note: try to avoid naming ctns/ctxs/etc as "current" and "pending".
|
||||||
|
|||||||
@@ -850,14 +850,16 @@ class LNWallet(LNWorker):
|
|||||||
self._channels = {} # type: Dict[bytes, Channel]
|
self._channels = {} # type: Dict[bytes, Channel]
|
||||||
channels = self.db.get_dict("channels")
|
channels = self.db.get_dict("channels")
|
||||||
for channel_id, c in random_shuffled_copy(channels.items()):
|
for channel_id, c in random_shuffled_copy(channels.items()):
|
||||||
self._channels[bfh(channel_id)] = Channel(c, lnworker=self)
|
self._channels[bfh(channel_id)] = chan = Channel(c, lnworker=self)
|
||||||
|
self.wallet.set_reserved_addresses_for_chan(chan, reserved=True)
|
||||||
|
|
||||||
self._channel_backups = {} # type: Dict[bytes, ChannelBackup]
|
self._channel_backups = {} # type: Dict[bytes, ChannelBackup]
|
||||||
# order is important: imported should overwrite onchain
|
# order is important: imported should overwrite onchain
|
||||||
for name in ["onchain_channel_backups", "imported_channel_backups"]:
|
for name in ["onchain_channel_backups", "imported_channel_backups"]:
|
||||||
channel_backups = self.db.get_dict(name)
|
channel_backups = self.db.get_dict(name)
|
||||||
for channel_id, storage in channel_backups.items():
|
for channel_id, storage in channel_backups.items():
|
||||||
self._channel_backups[bfh(channel_id)] = ChannelBackup(storage, lnworker=self)
|
self._channel_backups[bfh(channel_id)] = cb = ChannelBackup(storage, lnworker=self)
|
||||||
|
self.wallet.set_reserved_addresses_for_chan(cb, reserved=True)
|
||||||
|
|
||||||
self._paysessions = dict() # type: Dict[bytes, PaySession]
|
self._paysessions = dict() # type: Dict[bytes, PaySession]
|
||||||
self.sent_htlcs_info = dict() # type: Dict[SentHtlcKey, SentHtlcInfo]
|
self.sent_htlcs_info = dict() # type: Dict[SentHtlcKey, SentHtlcInfo]
|
||||||
@@ -1341,8 +1343,7 @@ class LNWallet(LNWorker):
|
|||||||
self.add_channel(chan)
|
self.add_channel(chan)
|
||||||
channels_db = self.db.get_dict('channels')
|
channels_db = self.db.get_dict('channels')
|
||||||
channels_db[chan.channel_id.hex()] = chan.storage
|
channels_db[chan.channel_id.hex()] = chan.storage
|
||||||
for addr in chan.get_wallet_addresses_channel_might_want_reserved():
|
self.wallet.set_reserved_addresses_for_chan(chan, reserved=True)
|
||||||
self.wallet.set_reserved_state_of_address(addr, reserved=True)
|
|
||||||
try:
|
try:
|
||||||
self.save_channel(chan)
|
self.save_channel(chan)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -2864,8 +2865,7 @@ class LNWallet(LNWorker):
|
|||||||
with self.lock:
|
with self.lock:
|
||||||
self._channels.pop(chan_id)
|
self._channels.pop(chan_id)
|
||||||
self.db.get('channels').pop(chan_id.hex())
|
self.db.get('channels').pop(chan_id.hex())
|
||||||
for addr in chan.get_wallet_addresses_channel_might_want_reserved():
|
self.wallet.set_reserved_addresses_for_chan(chan, reserved=False)
|
||||||
self.wallet.set_reserved_state_of_address(addr, reserved=False)
|
|
||||||
|
|
||||||
util.trigger_callback('channels_updated', self.wallet)
|
util.trigger_callback('channels_updated', self.wallet)
|
||||||
util.trigger_callback('wallet_updated', self.wallet)
|
util.trigger_callback('wallet_updated', self.wallet)
|
||||||
@@ -2998,6 +2998,7 @@ class LNWallet(LNWorker):
|
|||||||
with self.lock:
|
with self.lock:
|
||||||
cb = ChannelBackup(cb_storage, lnworker=self)
|
cb = ChannelBackup(cb_storage, lnworker=self)
|
||||||
self._channel_backups[channel_id] = cb
|
self._channel_backups[channel_id] = cb
|
||||||
|
self.wallet.set_reserved_addresses_for_chan(cb, reserved=True)
|
||||||
self.wallet.save_db()
|
self.wallet.save_db()
|
||||||
util.trigger_callback('channels_updated', self.wallet)
|
util.trigger_callback('channels_updated', self.wallet)
|
||||||
self.lnwatcher.add_channel(cb.funding_outpoint.to_str(), cb.get_funding_address())
|
self.lnwatcher.add_channel(cb.funding_outpoint.to_str(), cb.get_funding_address())
|
||||||
@@ -3025,6 +3026,7 @@ class LNWallet(LNWorker):
|
|||||||
raise Exception('Channel not found')
|
raise Exception('Channel not found')
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self._channel_backups.pop(channel_id)
|
self._channel_backups.pop(channel_id)
|
||||||
|
self.wallet.set_reserved_addresses_for_chan(chan, reserved=False)
|
||||||
self.wallet.save_db()
|
self.wallet.save_db()
|
||||||
util.trigger_callback('channels_updated', self.wallet)
|
util.trigger_callback('channels_updated', self.wallet)
|
||||||
|
|
||||||
@@ -3111,6 +3113,7 @@ class LNWallet(LNWorker):
|
|||||||
d = self.db.get_dict("onchain_channel_backups")
|
d = self.db.get_dict("onchain_channel_backups")
|
||||||
d[channel_id] = cb_storage
|
d[channel_id] = cb_storage
|
||||||
cb = ChannelBackup(cb_storage, lnworker=self)
|
cb = ChannelBackup(cb_storage, lnworker=self)
|
||||||
|
self.wallet.set_reserved_addresses_for_chan(cb, reserved=True)
|
||||||
self.wallet.save_db()
|
self.wallet.save_db()
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self._channel_backups[bfh(channel_id)] = cb
|
self._channel_backups[bfh(channel_id)] = cb
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ if TYPE_CHECKING:
|
|||||||
from .network import Network
|
from .network import Network
|
||||||
from .exchange_rate import FxThread
|
from .exchange_rate import FxThread
|
||||||
from .submarine_swaps import SwapData
|
from .submarine_swaps import SwapData
|
||||||
|
from .lnchannel import AbstractChannel
|
||||||
|
|
||||||
|
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
@@ -2032,13 +2033,20 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
|
|
||||||
def set_reserved_state_of_address(self, addr: str, *, reserved: bool) -> None:
|
def set_reserved_state_of_address(self, addr: str, *, reserved: bool) -> None:
|
||||||
if not self.is_mine(addr):
|
if not self.is_mine(addr):
|
||||||
|
# silently ignore non-ismine addresses
|
||||||
return
|
return
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
has_changed = (addr in self._reserved_addresses) != reserved
|
||||||
if reserved:
|
if reserved:
|
||||||
self._reserved_addresses.add(addr)
|
self._reserved_addresses.add(addr)
|
||||||
else:
|
else:
|
||||||
self._reserved_addresses.discard(addr)
|
self._reserved_addresses.discard(addr)
|
||||||
self.db.put('reserved_addresses', list(self._reserved_addresses))
|
if has_changed:
|
||||||
|
self.db.put('reserved_addresses', list(self._reserved_addresses))
|
||||||
|
|
||||||
|
def set_reserved_addresses_for_chan(self, chan: 'AbstractChannel', *, reserved: bool) -> None:
|
||||||
|
for addr in chan.get_wallet_addresses_channel_might_want_reserved():
|
||||||
|
self.set_reserved_state_of_address(addr, reserved=reserved)
|
||||||
|
|
||||||
def can_export(self):
|
def can_export(self):
|
||||||
return not self.is_watching_only() and hasattr(self.keystore, 'get_private_key')
|
return not self.is_watching_only() and hasattr(self.keystore, 'get_private_key')
|
||||||
|
|||||||
Reference in New Issue
Block a user