URI: 
       tclean up local/global features - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit eced61123dc49db80366d4988c8594683e100be8
   DIR parent 4d32478f301bf732b88c0a4be9200d9f26792ca7
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Tue,  9 Oct 2018 20:10:26 +0200
       
       clean up local/global features
       
       Diffstat:
         M electrum/lnbase.py                  |      23 +++++++++++++++++++----
         M electrum/lnrouter.py                |       6 +++---
         M electrum/lnutil.py                  |      43 +++++++++++++++++++++----------
       
       3 files changed, 52 insertions(+), 20 deletions(-)
       ---
   DIR diff --git a/electrum/lnbase.py b/electrum/lnbase.py
       t@@ -17,6 +17,7 @@ from typing import List
        import cryptography.hazmat.primitives.ciphers.aead as AEAD
        import aiorpcx
        
       +from .util import list_enabled_bits
        from . import bitcoin
        from . import ecc
        from .ecc import sig_string_from_r_and_s, get_r_and_s_from_sig_string
       t@@ -30,8 +31,9 @@ from .lnhtlc import HTLCStateMachine, RevokeAndAck
        from .lnutil import (Outpoint, ChannelConfig, LocalState,
                             RemoteState, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
                             funding_output_script, get_ecdh, get_per_commitment_secret_from_seed,
       -                     secret_to_pubkey, LNPeerAddr, PaymentFailure,
       -                     LOCAL, REMOTE, HTLCOwner, generate_keypair, LnKeyFamily)
       +                     secret_to_pubkey, LNPeerAddr, PaymentFailure, LnLocalFeatures,
       +                     LOCAL, REMOTE, HTLCOwner, generate_keypair, LnKeyFamily,
       +                     get_ln_flag_pair_of_bit)
        from .lnrouter import NotFoundChanAnnouncementForUpdate, RouteEdge
        
        
       t@@ -290,7 +292,10 @@ class Peer(PrintError):
                self.announcement_signatures = defaultdict(asyncio.Queue)
                self.closing_signed = defaultdict(asyncio.Queue)
                self.payment_preimages = defaultdict(asyncio.Queue)
       -        self.localfeatures = (0x08 if request_initial_sync else 0)
       +        self.localfeatures = LnLocalFeatures(0)
       +        if request_initial_sync:
       +            self.localfeatures |= LnLocalFeatures.INITIAL_ROUTING_SYNC
       +        self.localfeatures |= LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
                self.invoices = lnworker.invoices
                self.attempted_route = {}
        
       t@@ -442,7 +447,17 @@ class Peer(PrintError):
                self.network.trigger_callback('ln_status')
        
            def on_init(self, payload):
       -        pass
       +        # 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
       +        our_flags = set(list_enabled_bits(self.localfeatures))
       +        their_flags = set(list_enabled_bits(int.from_bytes(payload['localfeatures'], byteorder="big")))
       +        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 LightningPeerConnectionClosed("remote does not have even flag {}"
       +                                                        .format(str(LnLocalFeatures(1 << flag))))
       +                self.localfeatures ^= 1 << flag  # disable flag
        
            def on_channel_update(self, payload):
                try:
   DIR diff --git a/electrum/lnrouter.py b/electrum/lnrouter.py
       t@@ -39,7 +39,7 @@ from .storage import JsonDB
        from .lnchannelverifier import LNChannelVerifier, verify_sig_for_channel_update
        from .crypto import Hash
        from . import ecc
       -from .lnutil import LN_GLOBAL_FEATURE_BITS, LNPeerAddr
       +from .lnutil import LN_GLOBAL_FEATURES_KNOWN_SET, LNPeerAddr
        
        
        class UnknownEvenFeatureBits(Exception): pass
       t@@ -55,7 +55,7 @@ class ChannelInfo(PrintError):
                self.features = channel_announcement_payload['features']
                enabled_features = list_enabled_bits(int.from_bytes(self.features, "big"))
                for fbit in enabled_features:
       -            if fbit not in LN_GLOBAL_FEATURE_BITS and fbit % 2 == 0:
       +            if (1 << fbit) not in LN_GLOBAL_FEATURES_KNOWN_SET and fbit % 2 == 0:
                        raise UnknownEvenFeatureBits()
        
                self.channel_id = channel_announcement_payload['short_channel_id']
       t@@ -191,7 +191,7 @@ class NodeInfo(PrintError):
                self.features = node_announcement_payload['features']
                enabled_features = list_enabled_bits(int.from_bytes(self.features, "big"))
                for fbit in enabled_features:
       -            if fbit not in LN_GLOBAL_FEATURE_BITS and fbit % 2 == 0:
       +            if (1 << fbit) not in LN_GLOBAL_FEATURES_KNOWN_SET and fbit % 2 == 0:
                        raise UnknownEvenFeatureBits()
                if not addresses_already_parsed:
                    self.addresses = self.parse_addresses_field(node_announcement_payload['addresses'])
   DIR diff --git a/electrum/lnutil.py b/electrum/lnutil.py
       t@@ -426,19 +426,36 @@ def get_ecdh(priv: bytes, pub: bytes) -> bytes:
            return sha256(pt.get_public_key_bytes())
        
        
       -LN_LOCAL_FEATURE_BITS = {
       -    0: 'option_data_loss_protect_req',
       -    1: 'option_data_loss_protect_opt',
       -    3: 'initial_routing_sync',
       -    4: 'option_upfront_shutdown_script_req',
       -    5: 'option_upfront_shutdown_script_opt',
       -    6: 'gossip_queries_req',
       -    7: 'gossip_queries_opt',
       -}
       -LN_LOCAL_FEATURE_BITS_INV = inv_dict(LN_LOCAL_FEATURE_BITS)
       -
       -LN_GLOBAL_FEATURE_BITS = {}
       -LN_GLOBAL_FEATURE_BITS_INV = inv_dict(LN_GLOBAL_FEATURE_BITS)
       +class LnLocalFeatures(IntFlag):
       +    OPTION_DATA_LOSS_PROTECT_REQ = 1 << 0
       +    OPTION_DATA_LOSS_PROTECT_OPT = 1 << 1
       +    INITIAL_ROUTING_SYNC = 1 << 3
       +    OPTION_UPFRONT_SHUTDOWN_SCRIPT_REQ = 1 << 4
       +    OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT = 1 << 5
       +    GOSSIP_QUERIES_REQ = 1 << 6
       +    GOSSIP_QUERIES_OPT = 1 << 7
       +
       +# note that these are powers of two, not the bits themselves
       +LN_LOCAL_FEATURES_KNOWN_SET = set(LnLocalFeatures)
       +
       +
       +def get_ln_flag_pair_of_bit(flag_bit: int):
       +    """Ln Feature flags are assigned in pairs, one even, one odd. See BOLT-09.
       +    Return the other flag from the pair.
       +    e.g. 6 -> 7
       +    e.g. 7 -> 6
       +    """
       +    if flag_bit % 2 == 0:
       +        return flag_bit + 1
       +    else:
       +        return flag_bit - 1
       +
       +
       +class LnGlobalFeatures(IntFlag):
       +    pass
       +
       +# note that these are powers of two, not the bits themselves
       +LN_GLOBAL_FEATURES_KNOWN_SET = set(LnGlobalFeatures)
        
        
        class LNPeerAddr(namedtuple('LNPeerAddr', ['host', 'port', 'pubkey'])):