URI: 
       tselect peers with desired features before connecting - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 86d1e50469377f8e6bc118ec6118ce1e187d3414
   DIR parent a96aa68a4c8f6b26d75b31404df25a3b34f65c8a
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Fri, 21 Feb 2020 10:57:13 +0100
       
       select peers with desired features before connecting
       
       Diffstat:
         M electrum/lnpeer.py                  |      23 ++++++-----------------
         M electrum/lnutil.py                  |      19 +++++++++++++++++++
         M electrum/lnworker.py                |      18 +++++++++++++++++-
       
       3 files changed, 42 insertions(+), 18 deletions(-)
       ---
   DIR diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
       t@@ -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,
       t@@ -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,
       t@@ -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
   DIR diff --git a/electrum/lnutil.py b/electrum/lnutil.py
       t@@ -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)
       t@@ -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:
        
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -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
       t@@ -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()
       t@@ -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())
       t@@ -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]