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:
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]