diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py index 0da725e9f..163ee23ac 100644 --- a/electrum/lnwatcher.py +++ b/electrum/lnwatcher.py @@ -11,7 +11,7 @@ from typing import NamedTuple, Dict from . import util from .sql_db import SqlDB, sql from .wallet_db import WalletDB -from .util import bh2u, bfh, log_exceptions, ignore_exceptions, TxMinedInfo +from .util import bh2u, bfh, log_exceptions, ignore_exceptions, TxMinedInfo, random_shuffled_copy from .address_synchronizer import AddressSynchronizer, TX_HEIGHT_LOCAL, TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED from .transaction import Transaction, TxOutpoint @@ -278,8 +278,8 @@ class WatchTower(LNWatcher): async def start_watching(self): # I need to watch the addresses from sweepstore - l = await self.sweepstore.list_channels() - for outpoint, address in l: + lst = await self.sweepstore.list_channels() + for outpoint, address in random_shuffled_copy(lst): self.add_channel(outpoint, address) async def do_breach_remedy(self, funding_outpoint, closing_tx, spenders): diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 02cd671ce..fbb97b09b 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -35,7 +35,7 @@ from .crypto import sha256 from .bip32 import BIP32Node from .util import bh2u, bfh, InvoiceError, resolve_dns_srv, is_ip_address, log_exceptions from .util import ignore_exceptions, make_aiohttp_session, SilentTaskGroup -from .util import timestamp_to_datetime +from .util import timestamp_to_datetime, random_shuffled_copy from .util import MyEncoder from .logging import Logger from .lntransport import LNTransport, LNResponderTransport @@ -499,7 +499,7 @@ class LNWallet(LNWorker): # note: accessing channels (besides simple lookup) needs self.lock! self._channels = {} # type: Dict[bytes, Channel] channels = self.db.get_dict("channels") - for channel_id, c in channels.items(): + for channel_id, c in random_shuffled_copy(channels.items()): self._channels[bfh(channel_id)] = Channel(c, sweep_address=self.sweep_address, lnworker=self) self.pending_payments = defaultdict(asyncio.Future) # type: Dict[bytes, asyncio.Future[BarePaymentAttemptLog]] @@ -1397,7 +1397,7 @@ class LNBackups(Logger): self.wallet = wallet self.db = wallet.db self.channel_backups = {} - for channel_id, cb in self.db.get_dict("channel_backups").items(): + for channel_id, cb in random_shuffled_copy(self.db.get_dict("channel_backups").items()): self.channel_backups[bfh(channel_id)] = ChannelBackup(cb, sweep_address=self.sweep_address, lnworker=self) @property diff --git a/electrum/synchronizer.py b/electrum/synchronizer.py index 6363eeab8..e922ebc3e 100644 --- a/electrum/synchronizer.py +++ b/electrum/synchronizer.py @@ -32,7 +32,7 @@ from aiorpcx import TaskGroup, run_in_thread, RPCError from . import util from .transaction import Transaction, PartialTransaction -from .util import bh2u, make_aiohttp_session, NetworkJobOnDefaultServer +from .util import bh2u, make_aiohttp_session, NetworkJobOnDefaultServer, random_shuffled_copy from .bitcoin import address_to_scripthash, is_address from .network import UntrustedServerReturnedError from .logging import Logger @@ -240,7 +240,7 @@ class Synchronizer(SynchronizerBase): if history == ['*']: continue await self._request_missing_txs(history, allow_server_not_finding_tx=True) # add addresses to bootstrap - for addr in self.wallet.get_addresses(): + for addr in random_shuffled_copy(self.wallet.get_addresses()): await self._add_address(addr) # main loop while True: diff --git a/electrum/util.py b/electrum/util.py index 36296ff3c..e0b0fe092 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -24,7 +24,7 @@ import binascii import os, sys, re, json from collections import defaultdict, OrderedDict from typing import (NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable, Any, - Sequence, Dict, Generic, TypeVar) + Sequence, Dict, Generic, TypeVar, List, Iterable) from datetime import datetime import decimal from decimal import Decimal @@ -1398,3 +1398,12 @@ class JsonRPCClient: async def coro(*args): return await self.request(endpoint, *args) setattr(self, endpoint, coro) + + +T = TypeVar('T') + +def random_shuffled_copy(x: Iterable[T]) -> List[T]: + """Returns a shuffled copy of the input.""" + x_copy = list(x) # copy + random.shuffle(x_copy) # shuffle in-place + return x_copy