electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit fa0ef9c5481861484b504dab5c7eb703c5edb49f
parent 942e03e3ae59d6bd8900d6c63a2c52b19fc5bf04
Author: SomberNight <somber.night@protonmail.com>
Date:   Fri,  6 Mar 2020 03:37:00 +0100

ln: store network addresses for channel counterparties in channels

So we can reconnect to them without relying on gossip db.

Diffstat:
Melectrum/lnchannel.py | 20++++++++++++++++++--
Melectrum/lnpeer.py | 6++++++
Melectrum/lnworker.py | 27++++++++++++++-------------
3 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py @@ -27,10 +27,12 @@ from collections import namedtuple, defaultdict import binascii import json from enum import IntEnum -from typing import Optional, Dict, List, Tuple, NamedTuple, Set, Callable, Iterable, Sequence, TYPE_CHECKING +from typing import Optional, Dict, List, Tuple, NamedTuple, Set, Callable, Iterable, Sequence, TYPE_CHECKING, Iterator import time import threading +from aiorpcx import NetAddress + from . import ecc from . import constants from .util import bfh, bh2u @@ -47,7 +49,7 @@ from .lnutil import (Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKey HTLC_TIMEOUT_WEIGHT, HTLC_SUCCESS_WEIGHT, extract_ctn_from_tx_and_chan, UpdateAddHtlc, funding_output_script, SENT, RECEIVED, LOCAL, REMOTE, HTLCOwner, make_commitment_outputs, ScriptHtlc, PaymentFailure, calc_onchain_fees, RemoteMisbehaving, make_htlc_output_witness_script, - ShortChannelID, map_htlcs_to_ctx_output_idxs) + ShortChannelID, map_htlcs_to_ctx_output_idxs, LNPeerAddr) from .lnsweep import create_sweeptxs_for_our_ctx, create_sweeptxs_for_their_ctx from .lnsweep import create_sweeptx_for_their_revoked_htlc, SweepInfo from .lnhtlc import HTLCManager @@ -183,6 +185,20 @@ class Channel(Logger): def get_remote_update(self) -> Optional[bytes]: return bfh(self.storage.get('remote_update')) if self.storage.get('remote_update') else None + def add_or_update_peer_addr(self, peer: LNPeerAddr) -> None: + if 'peer_network_addresses' not in self.storage: + self.storage['peer_network_addresses'] = {} + now = int(time.time()) + self.storage['peer_network_addresses'][peer.net_addr_str()] = now + + def get_peer_addresses(self) -> Iterator[LNPeerAddr]: + # sort by timestamp: most recent first + addrs = sorted(self.storage.get('peer_network_addresses', {}).items(), + key=lambda x: x[1], reverse=True) + for net_addr_str, ts in addrs: + net_addr = NetAddress.from_string(net_addr_str) + yield LNPeerAddr(host=str(net_addr.host), port=net_addr.port, pubkey=self.node_id) + def get_outgoing_gossip_channel_update(self) -> bytes: if self._outgoing_channel_update is not None: return self._outgoing_channel_update diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py @@ -202,6 +202,8 @@ class Peer(Logger): raise GracefulDisconnect(f"{str(e)}") if isinstance(self.transport, LNTransport): self.channel_db.add_recent_peer(self.transport.peer_addr) + for chan in self.channels.values(): + chan.add_or_update_peer_addr(self.transport.peer_addr) self._received_init = True self.maybe_set_initialized() @@ -597,6 +599,8 @@ class Peer(Logger): lnworker=self.lnworker, initial_feerate=feerate) chan.storage['funding_inputs'] = [txin.prevout.to_json() for txin in funding_tx.inputs()] + if isinstance(self.transport, LNTransport): + chan.add_or_update_peer_addr(self.transport.peer_addr) sig_64, _ = chan.sign_next_commitment() self.temp_id_to_id[temp_channel_id] = channel_id self.send_message("funding_created", @@ -695,6 +699,8 @@ class Peer(Logger): lnworker=self.lnworker, initial_feerate=feerate) chan.storage['init_timestamp'] = int(time.time()) + if isinstance(self.transport, LNTransport): + chan.add_or_update_peer_addr(self.transport.peer_addr) remote_sig = funding_created['signature'] chan.receive_new_commitment(remote_sig, []) sig_64, _ = chan.sign_next_commitment() diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -1313,24 +1313,25 @@ class LNWallet(LNWorker): @ignore_exceptions @log_exceptions - async def reestablish_peer_for_given_channel(self, chan): + async def reestablish_peer_for_given_channel(self, chan: Channel) -> None: now = time.time() - # try last good address first - peer = self.channel_db.get_last_good_address(chan.node_id) - if peer: + peer_addresses = [] + # will try last good address first, from gossip + last_good_addr = self.channel_db.get_last_good_address(chan.node_id) + if last_good_addr: + peer_addresses.append(last_good_addr) + # will try addresses for node_id from gossip + addrs_from_gossip = self.channel_db.get_node_addresses(chan.node_id) or [] + for host, port, ts in addrs_from_gossip: + peer_addresses.append(LNPeerAddr(host, port, chan.node_id)) + # will try addresses stored in channel storage + peer_addresses += list(chan.get_peer_addresses()) + # now select first one that has not failed recently + for peer in peer_addresses: last_tried = self._last_tried_peer.get(peer, 0) if last_tried + PEER_RETRY_INTERVAL_FOR_CHANNELS < now: await self._add_peer(peer.host, peer.port, peer.pubkey) return - # try random address for node_id - addresses = self.channel_db.get_node_addresses(chan.node_id) - if not addresses: - return - host, port, t = random.choice(list(addresses)) - peer = LNPeerAddr(host, port, chan.node_id) - last_tried = self._last_tried_peer.get(peer, 0) - if last_tried + PEER_RETRY_INTERVAL_FOR_CHANNELS < now: - await self._add_peer(host, port, chan.node_id) async def reestablish_peers_and_channels(self): while True: