commit eced61123dc49db80366d4988c8594683e100be8
parent 4d32478f301bf732b88c0a4be9200d9f26792ca7
Author: SomberNight <somber.night@protonmail.com>
Date: Tue, 9 Oct 2018 20:10:26 +0200
clean up local/global features
Diffstat:
3 files changed, 52 insertions(+), 20 deletions(-)
diff --git a/electrum/lnbase.py b/electrum/lnbase.py
@@ -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
@@ -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
@@ -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 = {}
@@ -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:
diff --git a/electrum/lnrouter.py b/electrum/lnrouter.py
@@ -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
@@ -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']
@@ -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'])
diff --git a/electrum/lnutil.py b/electrum/lnutil.py
@@ -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'])):