URI: 
       tDo not use network callback to update channel states; call LNWorker methods directly instead. A callback was used because a single LNWnwatcher object used to be shared for all wallets. Since wallet now have their own LNWatcher instance, this can be simplified. - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 3a35f90aa0510546b653d4b822f702e776f47dcc
   DIR parent 36f32651cc970b3429b56aa1a763374c50eee980
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sun, 16 Feb 2020 14:26:07 +0100
       
       Do not use network callback to update channel states; call LNWorker methods directly instead.
       A callback was used because a single LNWnwatcher object used to be shared for all wallets.
       Since wallet now have their own LNWatcher instance, this can be simplified.
       
       Diffstat:
         M electrum/lnwatcher.py               |     117 ++++++++++++++++++++++++++-----
         M electrum/lnworker.py                |      81 ++-----------------------------
       
       2 files changed, 106 insertions(+), 92 deletions(-)
       ---
   DIR diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py
       t@@ -178,28 +178,26 @@ class LNWatcher(AddressSynchronizer):
                keep_watching, spenders = self.inspect_tx_candidate(funding_outpoint, 0)
                funding_txid = funding_outpoint.split(':')[0]
                funding_height = self.get_tx_height(funding_txid)
       -        if funding_height.height == TX_HEIGHT_LOCAL:
       -            return
                closing_txid = spenders.get(funding_outpoint)
                closing_height = self.get_tx_height(closing_txid)
       -        if closing_height.height == TX_HEIGHT_LOCAL:
       -            self.network.trigger_callback('update_open_channel', funding_outpoint, funding_txid, funding_height)
       -        else:
       +        await self.update_channel_state(
       +            funding_outpoint, funding_txid,
       +            funding_height, closing_txid,
       +            closing_height, keep_watching)
       +        if closing_txid:
                    closing_tx = self.db.get_transaction(closing_txid)
       -            if not closing_tx:
       +            if closing_tx:
       +                await self.do_breach_remedy(funding_outpoint, closing_tx, spenders)
       +            else:
                        self.logger.info(f"channel {funding_outpoint} closed by {closing_txid}. still waiting for tx itself...")
       -                return
       -            self.network.trigger_callback('update_closed_channel', funding_outpoint, spenders,
       -                                          funding_txid, funding_height, closing_txid,
       -                                          closing_height, closing_tx, keep_watching)  # FIXME sooo many args..
       -            # TODO: add tests for local_watchtower
       -            await self.do_breach_remedy(funding_outpoint, spenders)
                if not keep_watching:
                    await self.unwatch_channel(address, funding_outpoint)
        
       -    async def do_breach_remedy(self, funding_outpoints, spenders):
       -        # overloaded in WatchTower
       -        pass
       +    async def do_breach_remedy(self, funding_outpoint, closing_tx, spenders):
       +        raise NotImplementedError() # implemented by subclasses
       +
       +    async def update_channel_state(self, *args):
       +        raise NotImplementedError() # implemented by subclasses
        
            def inspect_tx_candidate(self, outpoint, n):
                # FIXME: instead of stopping recursion at n == 2,
       t@@ -267,7 +265,7 @@ class WatchTower(LNWatcher):
                for outpoint, address in l:
                    self.add_channel(outpoint, address)
        
       -    async def do_breach_remedy(self, funding_outpoint, spenders):
       +    async def do_breach_remedy(self, funding_outpoint, closing_tx, spenders):
                for prevout, spender in spenders.items():
                    if spender is not None:
                        continue
       t@@ -315,3 +313,90 @@ class WatchTower(LNWatcher):
                await self.sweepstore.remove_channel(funding_outpoint)
                if funding_outpoint in self.tx_progress:
                    self.tx_progress[funding_outpoint].all_done.set()
       +
       +    async def update_channel_state(self, *args):
       +        pass
       +
       +
       +class LNWalletWatcher(LNWatcher):
       +
       +    def __init__(self, lnworker, network):
       +        LNWatcher.__init__(self, network)
       +        self.network = network
       +        self.lnworker = lnworker
       +
       +    @ignore_exceptions
       +    @log_exceptions
       +    async def update_channel_state(self, funding_outpoint, funding_txid, funding_height, closing_txid, closing_height, keep_watching):
       +        chan = self.lnworker.channel_by_txo(funding_outpoint)
       +        if not chan:
       +            return
       +        if funding_height.height == TX_HEIGHT_LOCAL:
       +            return
       +        elif closing_height.height == TX_HEIGHT_LOCAL:
       +            await self.lnworker.update_open_channel(chan, funding_txid, funding_height)
       +        else:
       +            await self.lnworker.update_closed_channel(chan, funding_txid, funding_height, closing_txid, closing_height, keep_watching)
       +
       +    async def do_breach_remedy(self, funding_outpoint, closing_tx, spenders):
       +        chan = self.lnworker.channel_by_txo(funding_outpoint)
       +        if not chan:
       +            return
       +        # detect who closed and set sweep_info
       +        sweep_info_dict = chan.sweep_ctx(closing_tx)
       +        self.logger.info(f'sweep_info_dict length: {len(sweep_info_dict)}')
       +        # create and broadcast transaction
       +        for prevout, sweep_info in sweep_info_dict.items():
       +            name = sweep_info.name
       +            spender_txid = spenders.get(prevout)
       +            if spender_txid is not None:
       +                # TODO handle exceptions for network.get_transaction
       +                # TODO don't do network request every time... save tx at least in memory, or maybe wallet file?
       +                spender_tx = await self.network.get_transaction(spender_txid)
       +                spender_tx = Transaction(spender_tx)
       +                e_htlc_tx = chan.sweep_htlc(closing_tx, spender_tx)
       +                if e_htlc_tx:
       +                    spender2 = spenders.get(spender_txid+':0')
       +                    if spender2:
       +                        self.logger.info(f'htlc is already spent {name}: {prevout}')
       +                    else:
       +                        self.logger.info(f'trying to redeem htlc {name}: {prevout}')
       +                        await self.try_redeem(spender_txid+':0', e_htlc_tx)
       +                else:
       +                    self.logger.info(f'outpoint already spent {name}: {prevout}')
       +            else:
       +                self.logger.info(f'trying to redeem {name}: {prevout}')
       +                await self.try_redeem(prevout, sweep_info)
       +
       +    @log_exceptions
       +    async def try_redeem(self, prevout: str, sweep_info: 'SweepInfo') -> None:
       +        name = sweep_info.name
       +        prev_txid, prev_index = prevout.split(':')
       +        broadcast = True
       +        if sweep_info.cltv_expiry:
       +            local_height = self.network.get_local_height()
       +            remaining = sweep_info.cltv_expiry - local_height
       +            if remaining > 0:
       +                self.logger.info('waiting for {}: CLTV ({} > {}), prevout {}'
       +                                 .format(name, local_height, sweep_info.cltv_expiry, prevout))
       +                broadcast = False
       +        if sweep_info.csv_delay:
       +            prev_height = self.get_tx_height(prev_txid)
       +            remaining = sweep_info.csv_delay - prev_height.conf
       +            if remaining > 0:
       +                self.logger.info('waiting for {}: CSV ({} >= {}), prevout: {}'
       +                                 .format(name, prev_height.conf, sweep_info.csv_delay, prevout))
       +                broadcast = False
       +        tx = sweep_info.gen_tx()
       +        if tx is None:
       +            self.logger.info(f'{name} could not claim output: {prevout}, dust')
       +        self.lnworker.wallet.set_label(tx.txid(), name)
       +        if broadcast:
       +            await self.network.try_broadcasting(tx, name)
       +        else:
       +            # it's OK to add local transaction, the fee will be recomputed
       +            try:
       +                self.lnworker.wallet.add_future_tx(tx, remaining)
       +                self.logger.info(f'adding future tx: {name}. prevout: {prevout}')
       +            except Exception as e:
       +                self.logger.info(f'could not add future tx: {name}. prevout: {prevout} {str(e)}')
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -61,7 +61,7 @@ from .i18n import _
        from .lnrouter import RouteEdge, LNPaymentRoute, is_route_sane_to_use
        from .address_synchronizer import TX_HEIGHT_LOCAL
        from . import lnsweep
       -from .lnwatcher import LNWatcher
       +from .lnwatcher import LNWalletWatcher
        
        if TYPE_CHECKING:
            from .network import Network
       t@@ -408,12 +408,10 @@ class LNWallet(LNWorker):
        
            def start_network(self, network: 'Network'):
                assert network
       -        self.lnwatcher = LNWatcher(network)
       +        self.lnwatcher = LNWalletWatcher(self, network)
                self.lnwatcher.start_network(network)
                self.network = network
                daemon = network.daemon
       -        self.network.register_callback(self.on_update_open_channel, ['update_open_channel'])
       -        self.network.register_callback(self.on_update_closed_channel, ['update_closed_channel'])
                for chan_id, chan in self.channels.items():
                    self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address())
        
       t@@ -651,17 +649,10 @@ class LNWallet(LNWorker):
                    if chan.funding_outpoint.to_str() == txo:
                        return chan
        
       -    @ignore_exceptions
       -    @log_exceptions
       -    async def on_update_open_channel(self, event, funding_outpoint, funding_txid, funding_height):
       -        chan = self.channel_by_txo(funding_outpoint)
       -        if not chan:
       -            return
       -
       +    async def update_open_channel(self, chan, funding_txid, funding_height):
                # return early to prevent overwriting closing_txid with None
                if chan.is_closed():
                    return
       -
                # save timestamp regardless of state, so that funding tx is returned in get_history
                self.channel_timestamps[bh2u(chan.channel_id)] = chan.funding_outpoint.txid, funding_height.height, funding_height.timestamp, None, None, None
        
       t@@ -699,12 +690,8 @@ class LNWallet(LNWorker):
                        self.logger.info('REBROADCASTING CLOSING TX')
                        await self.network.try_broadcasting(force_close_tx, 'force-close')
        
       -    @ignore_exceptions
       -    @log_exceptions
       -    async def on_update_closed_channel(self, event, funding_outpoint, spenders, funding_txid, funding_height, closing_txid, closing_height, closing_tx, keep_watching):
       -        chan = self.channel_by_txo(funding_outpoint)
       -        if not chan:
       -            return
       +
       +    async def update_closed_channel(self, chan, funding_txid, funding_height, closing_txid, closing_height, keep_watching):
        
                # fixme: this is wasteful
                self.channel_timestamps[bh2u(chan.channel_id)] = funding_txid, funding_height.height, funding_height.timestamp, closing_txid, closing_height.height, closing_height.timestamp
       t@@ -719,64 +706,6 @@ class LNWallet(LNWorker):
                if chan.get_state() == channel_states.CLOSED and not keep_watching:
                    chan.set_state(channel_states.REDEEMED)
        
       -        # detect who closed and set sweep_info
       -        sweep_info_dict = chan.sweep_ctx(closing_tx)
       -        self.logger.info(f'sweep_info_dict length: {len(sweep_info_dict)}')
       -        # create and broadcast transaction
       -        for prevout, sweep_info in sweep_info_dict.items():
       -            name = sweep_info.name
       -            spender_txid = spenders.get(prevout)
       -            if spender_txid is not None:
       -                # TODO handle exceptions for network.get_transaction
       -                # TODO don't do network request every time... save tx at least in memory, or maybe wallet file?
       -                spender_tx = await self.network.get_transaction(spender_txid)
       -                spender_tx = Transaction(spender_tx)
       -                e_htlc_tx = chan.sweep_htlc(closing_tx, spender_tx)
       -                if e_htlc_tx:
       -                    spender2 = spenders.get(spender_txid+':0')
       -                    if spender2:
       -                        self.logger.info(f'htlc is already spent {name}: {prevout}')
       -                    else:
       -                        self.logger.info(f'trying to redeem htlc {name}: {prevout}')
       -                        await self.try_redeem(spender_txid+':0', e_htlc_tx)
       -                else:
       -                    self.logger.info(f'outpoint already spent {name}: {prevout}')
       -            else:
       -                self.logger.info(f'trying to redeem {name}: {prevout}')
       -                await self.try_redeem(prevout, sweep_info)
       -
       -    @log_exceptions
       -    async def try_redeem(self, prevout: str, sweep_info: 'SweepInfo') -> None:
       -        name = sweep_info.name
       -        prev_txid, prev_index = prevout.split(':')
       -        broadcast = True
       -        if sweep_info.cltv_expiry:
       -            local_height = self.network.get_local_height()
       -            remaining = sweep_info.cltv_expiry - local_height
       -            if remaining > 0:
       -                self.logger.info('waiting for {}: CLTV ({} > {}), prevout {}'
       -                                 .format(name, local_height, sweep_info.cltv_expiry, prevout))
       -                broadcast = False
       -        if sweep_info.csv_delay:
       -            prev_height = self.lnwatcher.get_tx_height(prev_txid)
       -            remaining = sweep_info.csv_delay - prev_height.conf
       -            if remaining > 0:
       -                self.logger.info('waiting for {}: CSV ({} >= {}), prevout: {}'
       -                                 .format(name, prev_height.conf, sweep_info.csv_delay, prevout))
       -                broadcast = False
       -        tx = sweep_info.gen_tx()
       -        if tx is None:
       -            self.logger.info(f'{name} could not claim output: {prevout}, dust')
       -        self.wallet.set_label(tx.txid(), name)
       -        if broadcast:
       -            await self.network.try_broadcasting(tx, name)
       -        else:
       -            # it's OK to add local transaction, the fee will be recomputed
       -            try:
       -                self.wallet.add_future_tx(tx, remaining)
       -                self.logger.info(f'adding future tx: {name}. prevout: {prevout}')
       -            except Exception as e:
       -                self.logger.info(f'could not add future tx: {name}. prevout: {prevout} {str(e)}')
        
            def should_channel_be_closed_due_to_expiring_htlcs(self, chan: Channel) -> bool:
                local_height = self.network.get_local_height()