Browse Source

select peers with desired features before connecting

hard-fail-on-bad-server-string
ThomasV 5 years ago
parent
commit
86d1e50469
  1. 23
      electrum/lnpeer.py
  2. 19
      electrum/lnutil.py
  3. 18
      electrum/lnworker.py

23
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

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

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

Loading…
Cancel
Save