Browse Source

network: randomise the order of address subscriptions

Before this, we were subscribing to our addresses in their bip32 order,
leaking this information to servers. While this leak seems mostly harmless,
it is trivial to fix.
bip39-recovery
SomberNight 5 years ago
parent
commit
2c962abe51
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 6
      electrum/lnwatcher.py
  2. 6
      electrum/lnworker.py
  3. 4
      electrum/synchronizer.py
  4. 11
      electrum/util.py

6
electrum/lnwatcher.py

@ -11,7 +11,7 @@ from typing import NamedTuple, Dict
from . import util from . import util
from .sql_db import SqlDB, sql from .sql_db import SqlDB, sql
from .wallet_db import WalletDB 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 .address_synchronizer import AddressSynchronizer, TX_HEIGHT_LOCAL, TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED
from .transaction import Transaction, TxOutpoint from .transaction import Transaction, TxOutpoint
@ -278,8 +278,8 @@ class WatchTower(LNWatcher):
async def start_watching(self): async def start_watching(self):
# I need to watch the addresses from sweepstore # I need to watch the addresses from sweepstore
l = await self.sweepstore.list_channels() lst = await self.sweepstore.list_channels()
for outpoint, address in l: for outpoint, address in random_shuffled_copy(lst):
self.add_channel(outpoint, address) self.add_channel(outpoint, address)
async def do_breach_remedy(self, funding_outpoint, closing_tx, spenders): async def do_breach_remedy(self, funding_outpoint, closing_tx, spenders):

6
electrum/lnworker.py

@ -35,7 +35,7 @@ from .crypto import sha256
from .bip32 import BIP32Node from .bip32 import BIP32Node
from .util import bh2u, bfh, InvoiceError, resolve_dns_srv, is_ip_address, log_exceptions 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 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 .util import MyEncoder
from .logging import Logger from .logging import Logger
from .lntransport import LNTransport, LNResponderTransport from .lntransport import LNTransport, LNResponderTransport
@ -499,7 +499,7 @@ class LNWallet(LNWorker):
# note: accessing channels (besides simple lookup) needs self.lock! # note: accessing channels (besides simple lookup) needs self.lock!
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 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._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]] self.pending_payments = defaultdict(asyncio.Future) # type: Dict[bytes, asyncio.Future[BarePaymentAttemptLog]]
@ -1397,7 +1397,7 @@ class LNBackups(Logger):
self.wallet = wallet self.wallet = wallet
self.db = wallet.db self.db = wallet.db
self.channel_backups = {} 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) self.channel_backups[bfh(channel_id)] = ChannelBackup(cb, sweep_address=self.sweep_address, lnworker=self)
@property @property

4
electrum/synchronizer.py

@ -32,7 +32,7 @@ from aiorpcx import TaskGroup, run_in_thread, RPCError
from . import util from . import util
from .transaction import Transaction, PartialTransaction 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 .bitcoin import address_to_scripthash, is_address
from .network import UntrustedServerReturnedError from .network import UntrustedServerReturnedError
from .logging import Logger from .logging import Logger
@ -240,7 +240,7 @@ class Synchronizer(SynchronizerBase):
if history == ['*']: continue if history == ['*']: continue
await self._request_missing_txs(history, allow_server_not_finding_tx=True) await self._request_missing_txs(history, allow_server_not_finding_tx=True)
# add addresses to bootstrap # 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) await self._add_address(addr)
# main loop # main loop
while True: while True:

11
electrum/util.py

@ -24,7 +24,7 @@ import binascii
import os, sys, re, json import os, sys, re, json
from collections import defaultdict, OrderedDict from collections import defaultdict, OrderedDict
from typing import (NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable, Any, 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 from datetime import datetime
import decimal import decimal
from decimal import Decimal from decimal import Decimal
@ -1398,3 +1398,12 @@ class JsonRPCClient:
async def coro(*args): async def coro(*args):
return await self.request(endpoint, *args) return await self.request(endpoint, *args)
setattr(self, endpoint, coro) 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

Loading…
Cancel
Save