URI: 
       tlnworker: accessing self.channels needs lock - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit c63209fa9ad9739b8c06c0a9e7a698aeba3d5ada
   DIR parent 6c055e80ed4db18ba984e4c7b52db12b93821603
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Thu, 19 Sep 2019 18:17:03 +0200
       
       lnworker: accessing self.channels needs lock
       
       Diffstat:
         M electrum/lnworker.py                |      38 ++++++++++++++++++++-----------
       
       1 file changed, 25 insertions(+), 13 deletions(-)
       ---
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -317,6 +317,8 @@ class LNWallet(LNWorker):
                self.preimages = self.storage.get('lightning_preimages', {})      # RHASH -> preimage
                self.sweep_address = wallet.get_receiving_address()
                self.lock = threading.RLock()
       +
       +        # note: accessing channels (besides simple lookup) needs self.lock!
                self.channels = {}  # type: Dict[bytes, Channel]
                for x in wallet.storage.get("channels", []):
                    c = Channel(x, sweep_address=self.sweep_address, lnworker=self)
       t@@ -331,7 +333,9 @@ class LNWallet(LNWorker):
                watchtower = self.network.local_watchtower
                if watchtower:
                    while True:
       -                for chan in self.channels.values():
       +                with self.lock:
       +                    channels = list(self.channels.values())
       +                for chan in channels:
                            await self.sync_channel_with_watchtower(chan, watchtower.sweepstore)
                        await asyncio.sleep(5)
        
       t@@ -345,16 +349,19 @@ class LNWallet(LNWorker):
                        r = await super().request(*args, **kwargs)
                        return r.data.result
                while True:
       -            watchtower_url = self.config.get('watchtower_url')
       -            if watchtower_url:
       -                try:
       -                    async with aiohttp.ClientSession(loop=asyncio.get_event_loop()) as session:
       -                        watchtower = myAiohttpClient(session, watchtower_url)
       -                        for chan in self.channels.values():
       -                            await self.sync_channel_with_watchtower(chan, watchtower)
       -                except aiohttp.client_exceptions.ClientConnectorError:
       -                    self.logger.info(f'could not contact remote watchtower {watchtower_url}')
                    await asyncio.sleep(5)
       +            watchtower_url = self.config.get('watchtower_url')
       +            if not watchtower_url:
       +                continue
       +            with self.lock:
       +                channels = list(self.channels.values())
       +            try:
       +                async with aiohttp.ClientSession(loop=asyncio.get_event_loop()) as session:
       +                    watchtower = myAiohttpClient(session, watchtower_url)
       +                    for chan in channels:
       +                        await self.sync_channel_with_watchtower(chan, watchtower)
       +            except aiohttp.client_exceptions.ClientConnectorError:
       +                self.logger.info(f'could not contact remote watchtower {watchtower_url}')
        
            async def sync_channel_with_watchtower(self, chan: Channel, watchtower):
                outpoint = chan.funding_outpoint.to_str()
       t@@ -416,7 +423,9 @@ class LNWallet(LNWorker):
                # return one item per payment_hash
                # note: with AMP we will have several channels per payment
                out = defaultdict(list)
       -        for chan in self.channels.values():
       +        with self.lock:
       +            channels = list(self.channels.values())
       +        for chan in channels:
                    d = chan.get_payments()
                    for k, v in d.items():
                        out[k].append(v)
       t@@ -484,7 +493,9 @@ class LNWallet(LNWorker):
                    }
                    out.append(item)
                # add funding events
       -        for chan in self.channels.values():
       +        with self.lock:
       +            channels = list(self.channels.values())
       +        for chan in channels:
                    item = self.channel_timestamps.get(chan.channel_id.hex())
                    if item is None:
                        continue
       t@@ -1129,7 +1140,8 @@ class LNWallet(LNWorker):
                # TODO: assert that closing tx is deep-mined and htlcs are swept
                chan = self.channels[chan_id]
                assert chan.is_closed()
       -        self.channels.pop(chan_id)
       +        with self.lock:
       +            self.channels.pop(chan_id)
                self.save_channels()
                self.network.trigger_callback('channels', self.wallet)
                self.network.trigger_callback('wallet_updated', self.wallet)