lnutil+lnwire: implement ChannelType
This commit is contained in:
@@ -8,7 +8,6 @@ import json
|
|||||||
from collections import namedtuple, defaultdict
|
from collections import namedtuple, defaultdict
|
||||||
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
|
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
import attr
|
import attr
|
||||||
from aiorpcx import NetAddress
|
from aiorpcx import NetAddress
|
||||||
|
|
||||||
@@ -1029,6 +1028,12 @@ class LnFeatures(IntFlag):
|
|||||||
_ln_feature_contexts[OPTION_SHUTDOWN_ANYSEGWIT_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
|
_ln_feature_contexts[OPTION_SHUTDOWN_ANYSEGWIT_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
|
||||||
_ln_feature_contexts[OPTION_SHUTDOWN_ANYSEGWIT_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
|
_ln_feature_contexts[OPTION_SHUTDOWN_ANYSEGWIT_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
|
||||||
|
|
||||||
|
OPTION_CHANNEL_TYPE_REQ = 1 << 44
|
||||||
|
OPTION_CHANNEL_TYPE_OPT = 1 << 45
|
||||||
|
|
||||||
|
_ln_feature_contexts[OPTION_CHANNEL_TYPE_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
|
||||||
|
_ln_feature_contexts[OPTION_CHANNEL_TYPE_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
|
||||||
|
|
||||||
# temporary
|
# temporary
|
||||||
OPTION_TRAMPOLINE_ROUTING_REQ_ECLAIR = 1 << 50
|
OPTION_TRAMPOLINE_ROUTING_REQ_ECLAIR = 1 << 50
|
||||||
OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR = 1 << 51
|
OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR = 1 << 51
|
||||||
@@ -1103,6 +1108,52 @@ class LnFeatures(IntFlag):
|
|||||||
or get_ln_flag_pair_of_bit(flag) in our_flags)
|
or get_ln_flag_pair_of_bit(flag) in our_flags)
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelType(IntFlag):
|
||||||
|
OPTION_LEGACY_CHANNEL = 0
|
||||||
|
OPTION_STATIC_REMOTEKEY = 1 << 12
|
||||||
|
OPTION_ANCHOR_OUTPUTS = 1 << 20
|
||||||
|
OPTION_ANCHORS_ZERO_FEE_HTLC_TX = 1 << 22
|
||||||
|
|
||||||
|
def discard_unknown_and_check(self):
|
||||||
|
"""Discards unknown flags and checks flag combination."""
|
||||||
|
flags = list_enabled_bits(self)
|
||||||
|
known_channel_types = []
|
||||||
|
for flag in flags:
|
||||||
|
channel_type = ChannelType(1 << flag)
|
||||||
|
if channel_type.name:
|
||||||
|
known_channel_types.append(channel_type)
|
||||||
|
final_channel_type = known_channel_types[0]
|
||||||
|
for channel_type in known_channel_types[1:]:
|
||||||
|
final_channel_type |= channel_type
|
||||||
|
|
||||||
|
final_channel_type.check_combinations()
|
||||||
|
return final_channel_type
|
||||||
|
|
||||||
|
def check_combinations(self):
|
||||||
|
if self == ChannelType.OPTION_STATIC_REMOTEKEY:
|
||||||
|
pass
|
||||||
|
elif self == ChannelType.OPTION_ANCHOR_OUTPUTS | ChannelType.OPTION_STATIC_REMOTEKEY:
|
||||||
|
pass
|
||||||
|
elif self == ChannelType.OPTION_ANCHORS_ZERO_FEE_HTLC_TX | ChannelType.OPTION_STATIC_REMOTEKEY:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ValueError("Channel type is not a valid flag combination.")
|
||||||
|
|
||||||
|
def complies_with_features(self, features: LnFeatures) -> bool:
|
||||||
|
flags = list_enabled_bits(self)
|
||||||
|
complies = True
|
||||||
|
for flag in flags:
|
||||||
|
feature = LnFeatures(1 << flag)
|
||||||
|
complies &= features.supports(feature)
|
||||||
|
return complies
|
||||||
|
|
||||||
|
def to_bytes_minimal(self):
|
||||||
|
# MUST use the smallest bitmap possible to represent the channel type.
|
||||||
|
bit_length =self.value.bit_length()
|
||||||
|
byte_length = bit_length // 8 + int(bool(bit_length % 8))
|
||||||
|
return self.to_bytes(byte_length, byteorder='big')
|
||||||
|
|
||||||
|
|
||||||
del LNFC # name is ambiguous without context
|
del LNFC # name is ambiguous without context
|
||||||
|
|
||||||
# features that are actually implemented and understood in our codebase:
|
# features that are actually implemented and understood in our codebase:
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ msgdata,open_channel,channel_flags,byte,
|
|||||||
msgdata,open_channel,tlvs,open_channel_tlvs,
|
msgdata,open_channel,tlvs,open_channel_tlvs,
|
||||||
tlvtype,open_channel_tlvs,upfront_shutdown_script,0
|
tlvtype,open_channel_tlvs,upfront_shutdown_script,0
|
||||||
tlvdata,open_channel_tlvs,upfront_shutdown_script,shutdown_scriptpubkey,byte,...
|
tlvdata,open_channel_tlvs,upfront_shutdown_script,shutdown_scriptpubkey,byte,...
|
||||||
|
tlvtype,open_channel_tlvs,channel_type,1
|
||||||
|
tlvdata,open_channel_tlvs,channel_type,type,byte,...
|
||||||
msgtype,accept_channel,33
|
msgtype,accept_channel,33
|
||||||
msgdata,accept_channel,temporary_channel_id,byte,32
|
msgdata,accept_channel,temporary_channel_id,byte,32
|
||||||
msgdata,accept_channel,dust_limit_satoshis,u64,
|
msgdata,accept_channel,dust_limit_satoshis,u64,
|
||||||
@@ -71,6 +73,8 @@ msgdata,accept_channel,first_per_commitment_point,point,
|
|||||||
msgdata,accept_channel,tlvs,accept_channel_tlvs,
|
msgdata,accept_channel,tlvs,accept_channel_tlvs,
|
||||||
tlvtype,accept_channel_tlvs,upfront_shutdown_script,0
|
tlvtype,accept_channel_tlvs,upfront_shutdown_script,0
|
||||||
tlvdata,accept_channel_tlvs,upfront_shutdown_script,shutdown_scriptpubkey,byte,...
|
tlvdata,accept_channel_tlvs,upfront_shutdown_script,shutdown_scriptpubkey,byte,...
|
||||||
|
tlvtype,accept_channel_tlvs,channel_type,1
|
||||||
|
tlvdata,accept_channel_tlvs,channel_type,type,byte,...
|
||||||
msgtype,funding_created,34
|
msgtype,funding_created,34
|
||||||
msgdata,funding_created,temporary_channel_id,byte,32
|
msgdata,funding_created,temporary_channel_id,byte,32
|
||||||
msgdata,funding_created,funding_txid,sha256,
|
msgdata,funding_created,funding_txid,sha256,
|
||||||
|
|||||||
|
@@ -9,7 +9,7 @@ from electrum.lnutil import (RevocationStore, get_per_commitment_secret_from_see
|
|||||||
derive_pubkey, make_htlc_tx, extract_ctn_from_tx, UnableToDeriveSecret,
|
derive_pubkey, make_htlc_tx, extract_ctn_from_tx, UnableToDeriveSecret,
|
||||||
get_compressed_pubkey_from_bech32, split_host_port, ConnStringFormatError,
|
get_compressed_pubkey_from_bech32, split_host_port, ConnStringFormatError,
|
||||||
ScriptHtlc, extract_nodeid, calc_fees_for_commitment_tx, UpdateAddHtlc, LnFeatures,
|
ScriptHtlc, extract_nodeid, calc_fees_for_commitment_tx, UpdateAddHtlc, LnFeatures,
|
||||||
ln_compare_features, IncompatibleLightningFeatures)
|
ln_compare_features, IncompatibleLightningFeatures, ChannelType)
|
||||||
from electrum.util import bh2u, bfh, MyEncoder
|
from electrum.util import bh2u, bfh, MyEncoder
|
||||||
from electrum.transaction import Transaction, PartialTransaction
|
from electrum.transaction import Transaction, PartialTransaction
|
||||||
from electrum.lnworker import LNWallet
|
from electrum.lnworker import LNWallet
|
||||||
@@ -890,3 +890,15 @@ class TestLNUtil(ElectrumTestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
None,
|
None,
|
||||||
LNWallet._decode_channel_update_msg(bytes.fromhex("0101") + msg_without_prefix))
|
LNWallet._decode_channel_update_msg(bytes.fromhex("0101") + msg_without_prefix))
|
||||||
|
|
||||||
|
def test_channel_type(self):
|
||||||
|
# test compliance and non compliance with LN features
|
||||||
|
features = LnFeatures(LnFeatures.BASIC_MPP_OPT | LnFeatures.OPTION_STATIC_REMOTEKEY_OPT)
|
||||||
|
self.assertTrue(ChannelType.OPTION_STATIC_REMOTEKEY.complies_with_features(features))
|
||||||
|
|
||||||
|
features = LnFeatures(LnFeatures.BASIC_MPP_OPT | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT)
|
||||||
|
self.assertFalse(ChannelType.OPTION_STATIC_REMOTEKEY.complies_with_features(features))
|
||||||
|
|
||||||
|
# ignore unknown channel types
|
||||||
|
channel_type = ChannelType(0b10000000001000000000010).discard_unknown_and_check()
|
||||||
|
self.assertEqual(ChannelType(0b10000000001000000000000), channel_type)
|
||||||
Reference in New Issue
Block a user