electrum

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

commit 86d1e50469377f8e6bc118ec6118ce1e187d3414
parent a96aa68a4c8f6b26d75b31404df25a3b34f65c8a
Author: ThomasV <thomasv@electrum.org>
Date:   Fri, 21 Feb 2020 10:57:13 +0100

select peers with desired features before connecting

Diffstat:
Melectrum/lnpeer.py | 23++++++-----------------
Melectrum/lnutil.py | 19+++++++++++++++++++
Melectrum/lnworker.py | 18+++++++++++++++++-
3 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py @@ -23,7 +23,7 @@ from . import bitcoin from . import ecc from .ecc import sig_string_from_r_and_s, get_r_and_s_from_sig_string, der_sig_from_sig_string from . import constants -from .util import bh2u, bfh, log_exceptions, list_enabled_bits, ignore_exceptions, chunks, SilentTaskGroup +from .util import bh2u, bfh, log_exceptions, ignore_exceptions, chunks, SilentTaskGroup from .transaction import Transaction, TxOutput, PartialTxOutput from .logging import Logger from .lnonion import (new_onion_packet, decode_onion_error, OnionFailureCode, calc_hops_data_for_payment, @@ -36,7 +36,7 @@ from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, funding_output_script, get_per_commitment_secret_from_seed, secret_to_pubkey, PaymentFailure, LnLocalFeatures, LOCAL, REMOTE, HTLCOwner, generate_keypair, LnKeyFamily, - get_ln_flag_pair_of_bit, privkey_to_pubkey, UnknownPaymentHash, MIN_FINAL_CLTV_EXPIRY_ACCEPTED, + ln_compare_features, privkey_to_pubkey, UnknownPaymentHash, MIN_FINAL_CLTV_EXPIRY_ACCEPTED, LightningPeerConnectionClosed, HandshakeFailed, NotFoundChanAnnouncementForUpdate, MINIMUM_MAX_HTLC_VALUE_IN_FLIGHT_ACCEPTED, MAXIMUM_HTLC_MINIMUM_MSAT_ACCEPTED, MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED, RemoteMisbehaving, DEFAULT_TO_SELF_DELAY, @@ -187,21 +187,10 @@ class Peer(Logger): # if they required some even flag we don't have, they will close themselves # but if we require an even flag they don't have, we close their_localfeatures = int.from_bytes(payload['localfeatures'], byteorder="big") - our_flags = set(list_enabled_bits(self.localfeatures)) - their_flags = set(list_enabled_bits(their_localfeatures)) - for flag in our_flags: - if flag not in their_flags and get_ln_flag_pair_of_bit(flag) not in their_flags: - # they don't have this feature we wanted :( - if flag % 2 == 0: # even flags are compulsory - raise GracefulDisconnect("remote does not support {}" - .format(str(LnLocalFeatures(1 << flag)))) - self.localfeatures ^= 1 << flag # disable flag - else: - # They too have this flag. - # For easier feature-bit-testing, if this is an even flag, we also - # set the corresponding odd flag now. - if flag % 2 == 0 and self.localfeatures & (1 << flag): - self.localfeatures |= 1 << get_ln_flag_pair_of_bit(flag) + try: + self.localfeatures = ln_compare_features(self.localfeatures, their_localfeatures) + except ValueError as e: + raise GracefulDisconnect(f"remote does not support {str(e)}") if isinstance(self.transport, LNTransport): self.channel_db.add_recent_peer(self.transport.peer_addr) self._received_init = True diff --git a/electrum/lnutil.py b/electrum/lnutil.py @@ -12,6 +12,7 @@ import attr from aiorpcx import NetAddress from .util import bfh, bh2u, inv_dict, UserFacingException +from .util import list_enabled_bits from .crypto import sha256 from .transaction import (Transaction, PartialTransaction, PartialTxInput, TxOutpoint, PartialTxOutput, opcodes, TxOutput) @@ -655,6 +656,24 @@ class LnGlobalFeatures(IntFlag): # note that these are powers of two, not the bits themselves LN_GLOBAL_FEATURES_KNOWN_SET = set(LnGlobalFeatures) +def ln_compare_features(our_features, their_features): + """raises ValueError if incompatible""" + our_flags = set(list_enabled_bits(our_features)) + their_flags = set(list_enabled_bits(their_features)) + for flag in our_flags: + if flag not in their_flags and get_ln_flag_pair_of_bit(flag) not in their_flags: + # they don't have this feature we wanted :( + if flag % 2 == 0: # even flags are compulsory + raise ValueError(LnLocalFeatures(1 << flag)) + our_features ^= 1 << flag # disable flag + else: + # They too have this flag. + # For easier feature-bit-testing, if this is an even flag, we also + # set the corresponding odd flag now. + if flag % 2 == 0 and our_features & (1 << flag): + our_features |= 1 << get_ln_flag_pair_of_bit(flag) + return our_features + class LNPeerAddr: diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -53,7 +53,7 @@ from .lnutil import (Outpoint, LNPeerAddr, NUM_MAX_EDGES_IN_PAYMENT_PATH, SENT, RECEIVED, HTLCOwner, UpdateAddHtlc, Direction, LnLocalFeatures, ShortChannelID, PaymentAttemptLog, PaymentAttemptFailureDetails) -from .lnutil import ln_dummy_address +from .lnutil import ln_dummy_address, ln_compare_features from .transaction import PartialTxOutput, PartialTransaction, PartialTxInput from .lnonion import OnionFailureCode from .lnmsg import decode_msg @@ -200,6 +200,18 @@ class LNWorker(Logger): self._add_peer(host, int(port), bfh(pubkey)), self.network.asyncio_loop) + def is_good_peer(self, peer): + node_id = peer.pubkey + node = self.channel_db._nodes.get(node_id) + if not node: + return False + try: + ln_compare_features(self.localfeatures, node.features) + except ValueError: + return False + #self.logger.info(f'is_good {peer.host}') + return True + async def _get_next_peers_to_try(self) -> Sequence[LNPeerAddr]: now = time.time() await self.channel_db.data_loaded.wait() @@ -215,6 +227,8 @@ class LNWorker(Logger): continue if peer in self._last_tried_peer: continue + if not self.is_good_peer(peer): + continue return [peer] # try random peer from graph unconnected_nodes = self.channel_db.get_200_randomly_sorted_nodes_not_in(self.peers.keys()) @@ -230,6 +244,8 @@ class LNWorker(Logger): continue if peer in self._last_tried_peer: continue + if not self.is_good_peer(peer): + continue #self.logger.info('taking random ln peer from our channel db') return [peer]