Browse Source

Merge pull request #7636 from bitromortac/2201-channel-type

lightning: implement channel types
patch-4
ThomasV 3 years ago
committed by GitHub
parent
commit
b268877d53
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      electrum/gui/kivy/uix/dialogs/lightning_channels.py
  2. 1
      electrum/gui/qt/channel_details.py
  3. 2
      electrum/json_db.py
  4. 8
      electrum/lnchannel.py
  5. 85
      electrum/lnpeer.py
  6. 58
      electrum/lnutil.py
  7. 4
      electrum/lnwire/peer_wire.csv
  8. 5
      electrum/lnworker.py
  9. 1
      electrum/tests/test_lnchannel.py
  10. 1
      electrum/tests/test_lnpeer.py
  11. 14
      electrum/tests/test_lnutil.py
  12. 20
      electrum/wallet_db.py

5
electrum/gui/kivy/uix/dialogs/lightning_channels.py

@ -246,6 +246,7 @@ Builder.load_string(r'''
warning: ''
is_frozen_for_sending: False
is_frozen_for_receiving: False
channel_type:''
BoxLayout:
padding: '12dp', '12dp', '12dp', '12dp'
spacing: '12dp'
@ -294,6 +295,9 @@ Builder.load_string(r'''
BoxLabel:
text: _('Frozen (for receiving)')
value: str(root.is_frozen_for_receiving)
BoxLabel:
text: _('Channel type')
value: str(root.channel_type)
Widget:
size_hint: 1, 0.1
TopLabel:
@ -484,6 +488,7 @@ class ChannelDetailsPopup(Popup, Logger):
self.warning = '' if self.app.wallet.lnworker.channel_db or self.app.wallet.lnworker.is_trampoline_peer(chan.node_id) else _('Warning') + ': ' + msg
self.is_frozen_for_sending = chan.is_frozen_for_sending()
self.is_frozen_for_receiving = chan.is_frozen_for_receiving()
self.channel_type = chan.storage['channel_type'].name_minimal
self.update_action_dropdown()
def update_action_dropdown(self):

1
electrum/gui/qt/channel_details.py

@ -193,6 +193,7 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin):
form_layout.addRow(_('Remote dust limit:'), self.dust_limit)
self.remote_reserve = self.window.format_amount_and_units(chan.config[REMOTE].reserve_sat)
form_layout.addRow(_('Remote reserve:'), SelectableLabel(self.remote_reserve))
form_layout.addRow(_('Channel type:'), SelectableLabel(chan.storage['channel_type'].name_minimal))
vbox.addLayout(form_layout)
# add htlc tree view to vbox (wouldn't scale correctly in QFormLayout)

2
electrum/json_db.py

@ -98,7 +98,7 @@ class StoredDict(dict):
if not self.db or self.db._should_convert_to_stored_dict(key):
v = StoredDict(v, self.db, self.path + [key])
# convert_value is called depth-first
if isinstance(v, dict) or isinstance(v, str):
if isinstance(v, dict) or isinstance(v, str) or isinstance(v, int):
if self.db:
v = self.db._convert_value(self.path, key, v)
# set parent of StoredObject

8
electrum/lnchannel.py

@ -52,7 +52,8 @@ from .lnutil import (Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKey
ScriptHtlc, PaymentFailure, calc_fees_for_commitment_tx, RemoteMisbehaving, make_htlc_output_witness_script,
ShortChannelID, map_htlcs_to_ctx_output_idxs, LNPeerAddr,
fee_for_htlc_output, offered_htlc_trim_threshold_sat,
received_htlc_trim_threshold_sat, make_commitment_output_to_remote_address)
received_htlc_trim_threshold_sat, make_commitment_output_to_remote_address,
ChannelType)
from .lnsweep import create_sweeptxs_for_our_ctx, create_sweeptxs_for_their_ctx
from .lnsweep import create_sweeptx_for_their_revoked_htlc, SweepInfo
from .lnhtlc import HTLCManager
@ -711,7 +712,8 @@ class Channel(AbstractChannel):
return chan_ann
def is_static_remotekey_enabled(self) -> bool:
return bool(self.storage.get('static_remotekey_enabled'))
channel_type = ChannelType(self.storage.get('channel_type'))
return bool(channel_type & ChannelType.OPTION_STATIC_REMOTEKEY)
def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]:
ret = []
@ -927,6 +929,7 @@ class Channel(AbstractChannel):
Action must be initiated by LOCAL.
Finally, the next remote ctx becomes the latest remote ctx.
"""
# TODO: when more channel types are supported, this method should depend on channel type
next_remote_ctn = self.get_next_ctn(REMOTE)
self.logger.info(f"sign_next_commitment {next_remote_ctn}")
@ -968,6 +971,7 @@ class Channel(AbstractChannel):
If all checks pass, the next local ctx becomes the latest local ctx.
"""
# TODO in many failure cases below, we should "fail" the channel (force-close)
# TODO: when more channel types are supported, this method should depend on channel type
next_local_ctn = self.get_next_ctn(LOCAL)
self.logger.info(f"receive_new_commitment. ctn={next_local_ctn}, len(htlc_sigs)={len(htlc_sigs)}")

85
electrum/lnpeer.py

@ -42,7 +42,7 @@ from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, ChannelConf
LightningPeerConnectionClosed, HandshakeFailed,
RemoteMisbehaving, ShortChannelID,
IncompatibleLightningFeatures, derive_payment_secret_from_payment_preimage,
UpfrontShutdownScriptViolation)
UpfrontShutdownScriptViolation, ChannelType)
from .lnutil import FeeUpdate, channel_id_from_funding_tx
from .lntransport import LNTransport, LNTransportBase
from .lnmsg import encode_msg, decode_msg, UnknownOptionalMsgType
@ -511,6 +511,9 @@ class Peer(Logger):
def is_static_remotekey(self):
return self.features.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT)
def is_channel_type(self):
return self.features.supports(LnFeatures.OPTION_CHANNEL_TYPE_OPT)
def is_upfront_shutdown_script(self):
return self.features.supports(LnFeatures.OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT)
@ -528,16 +531,15 @@ class Peer(Logger):
self.logger.info(f"upfront shutdown script received: {upfront_shutdown_script}")
return upfront_shutdown_script
def make_local_config(self, funding_sat: int, push_msat: int, initiator: HTLCOwner) -> LocalConfig:
def make_local_config(self, funding_sat: int, push_msat: int, initiator: HTLCOwner, channel_type: ChannelType) -> LocalConfig:
channel_seed = os.urandom(32)
initial_msat = funding_sat * 1000 - push_msat if initiator == LOCAL else push_msat
static_remotekey = None
# sending empty bytes as the upfront_shutdown_script will give us the
# flexibility to decide an address at closing time
upfront_shutdown_script = b''
if self.is_static_remotekey():
if channel_type & channel_type.OPTION_STATIC_REMOTEKEY:
wallet = self.lnworker.wallet
assert wallet.txin_type == 'p2wpkh'
addr = wallet.get_new_sweep_address_for_channel()
@ -613,7 +615,24 @@ class Peer(Logger):
raise Exception('Not a trampoline node: ' + str(self.their_features))
feerate = self.lnworker.current_feerate_per_kw()
local_config = self.make_local_config(funding_sat, push_msat, LOCAL)
# we set a channel type for internal bookkeeping
open_channel_tlvs = {}
if self.their_features.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT):
our_channel_type = ChannelType(ChannelType.OPTION_STATIC_REMOTEKEY)
else:
our_channel_type = ChannelType(0)
# if option_channel_type is negotiated: MUST set channel_type
if self.is_channel_type():
# if it includes channel_type: MUST set it to a defined type representing the type it wants.
open_channel_tlvs['channel_type'] = {
'type': our_channel_type.to_bytes_minimal()
}
local_config = self.make_local_config(funding_sat, push_msat, LOCAL, our_channel_type)
# if it includes open_channel_tlvs: MUST include upfront_shutdown_script.
open_channel_tlvs['upfront_shutdown_script'] = {
'shutdown_scriptpubkey': local_config.upfront_shutdown_script
}
# for the first commitment transaction
per_commitment_secret_first = get_per_commitment_secret_from_seed(
@ -642,10 +661,7 @@ class Peer(Logger):
channel_flags=0x00, # not willing to announce channel
channel_reserve_satoshis=local_config.reserve_sat,
htlc_minimum_msat=local_config.htlc_minimum_msat,
open_channel_tlvs={
'upfront_shutdown_script':
{'shutdown_scriptpubkey': local_config.upfront_shutdown_script}
}
open_channel_tlvs=open_channel_tlvs,
)
# <- accept_channel
@ -660,6 +676,15 @@ class Peer(Logger):
upfront_shutdown_script = self.upfront_shutdown_script_from_payload(
payload, 'accept')
accept_channel_tlvs = payload.get('accept_channel_tlvs')
their_channel_type = accept_channel_tlvs.get('channel_type') if accept_channel_tlvs else None
if their_channel_type:
their_channel_type = ChannelType.from_bytes(their_channel_type['type'], byteorder='big').discard_unknown_and_check()
# if channel_type is set, and channel_type was set in open_channel,
# and they are not equal types: MUST reject the channel.
if open_channel_tlvs.get('channel_type') is not None and their_channel_type != our_channel_type:
raise Exception("Channel type is not the one that we sent.")
remote_config = RemoteConfig(
payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
multisig_key=OnlyPubkeyKeypair(payload["funding_pubkey"]),
@ -675,7 +700,7 @@ class Peer(Logger):
htlc_minimum_msat=payload['htlc_minimum_msat'],
next_per_commitment_point=remote_per_commitment_point,
current_per_commitment_point=None,
upfront_shutdown_script=upfront_shutdown_script
upfront_shutdown_script=upfront_shutdown_script,
)
ChannelConfig.cross_validate_params(
local_config=local_config,
@ -724,7 +749,7 @@ class Peer(Logger):
funding_txn_minimum_depth=funding_txn_minimum_depth
)
storage = self.create_channel_storage(
channel_id, outpoint, local_config, remote_config, constraints)
channel_id, outpoint, local_config, remote_config, constraints, our_channel_type)
chan = Channel(
storage,
sweep_address=self.lnworker.sweep_address,
@ -755,7 +780,7 @@ class Peer(Logger):
self.lnworker.add_new_channel(chan)
return chan, funding_tx
def create_channel_storage(self, channel_id, outpoint, local_config, remote_config, constraints):
def create_channel_storage(self, channel_id, outpoint, local_config, remote_config, constraints, channel_type):
chan_dict = {
"node_id": self.pubkey.hex(),
"channel_id": channel_id.hex(),
@ -772,7 +797,7 @@ class Peer(Logger):
"fail_htlc_reasons": {}, # htlc_id -> onion_packet
"unfulfilled_htlcs": {}, # htlc_id -> error_bytes, failure_message
"revocation_store": {},
"static_remotekey_enabled": self.is_static_remotekey(), # stored because it cannot be "downgraded", per BOLT2
"channel_type": channel_type,
}
return StoredDict(chan_dict, self.lnworker.db if self.lnworker else None, [])
@ -796,7 +821,21 @@ class Peer(Logger):
push_msat = payload['push_msat']
feerate = payload['feerate_per_kw'] # note: we are not validating this
temp_chan_id = payload['temporary_channel_id']
local_config = self.make_local_config(funding_sat, push_msat, REMOTE)
open_channel_tlvs = payload.get('open_channel_tlvs')
channel_type = open_channel_tlvs.get('channel_type') if open_channel_tlvs else None
# The receiving node MAY fail the channel if:
# option_channel_type was negotiated but the message doesn't include a channel_type
if self.is_channel_type() and channel_type is None:
raise Exception("sender has advertized option_channel_type, but hasn't sent the channel type")
# MUST fail the channel if it supports channel_type,
# channel_type was set, and the type is not suitable.
elif self.is_channel_type() and channel_type is not None:
channel_type = ChannelType.from_bytes(channel_type['type'], byteorder='big').discard_unknown_and_check()
if not channel_type.complies_with_features(self.features):
raise Exception("sender has sent a channel type we don't support")
local_config = self.make_local_config(funding_sat, push_msat, REMOTE, channel_type)
upfront_shutdown_script = self.upfront_shutdown_script_from_payload(
payload, 'open')
@ -839,6 +878,17 @@ class Peer(Logger):
per_commitment_point_first = secret_to_pubkey(
int.from_bytes(per_commitment_secret_first, 'big'))
min_depth = 3
accept_channel_tlvs = {
'upfront_shutdown_script': {
'shutdown_scriptpubkey': local_config.upfront_shutdown_script
},
}
# The sender: if it sets channel_type: MUST set it to the channel_type from open_channel
if self.is_channel_type():
accept_channel_tlvs['channel_type'] = {
'type': channel_type.to_bytes_minimal()
}
self.send_message(
'accept_channel',
temporary_channel_id=temp_chan_id,
@ -855,10 +905,7 @@ class Peer(Logger):
delayed_payment_basepoint=local_config.delayed_basepoint.pubkey,
htlc_basepoint=local_config.htlc_basepoint.pubkey,
first_per_commitment_point=per_commitment_point_first,
accept_channel_tlvs={
'upfront_shutdown_script':
{'shutdown_scriptpubkey': local_config.upfront_shutdown_script}
}
accept_channel_tlvs=accept_channel_tlvs,
)
# <- funding created
@ -875,7 +922,7 @@ class Peer(Logger):
)
outpoint = Outpoint(funding_txid, funding_idx)
chan_dict = self.create_channel_storage(
channel_id, outpoint, local_config, remote_config, constraints)
channel_id, outpoint, local_config, remote_config, constraints, channel_type)
chan = Channel(
chan_dict,
sweep_address=self.lnworker.sweep_address,

58
electrum/lnutil.py

@ -8,7 +8,6 @@ import json
from collections import namedtuple, defaultdict
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
import re
import time
import attr
from aiorpcx import NetAddress
@ -1030,6 +1029,12 @@ class LnFeatures(IntFlag):
_ln_feature_contexts[OPTION_SHUTDOWN_ANYSEGWIT_REQ] = (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
OPTION_TRAMPOLINE_ROUTING_REQ_ECLAIR = 1 << 50
OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR = 1 << 51
@ -1104,6 +1109,56 @@ class LnFeatures(IntFlag):
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')
@property
def name_minimal(self):
return self.name.replace('OPTION_', '')
del LNFC # name is ambiguous without context
# features that are actually implemented and understood in our codebase:
@ -1119,6 +1174,7 @@ LN_FEATURES_IMPLEMENTED = (
| LnFeatures.BASIC_MPP_OPT | LnFeatures.BASIC_MPP_REQ
| LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT | LnFeatures.OPTION_TRAMPOLINE_ROUTING_REQ
| LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_OPT | LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_REQ
| LnFeatures.OPTION_CHANNEL_TYPE_OPT | LnFeatures.OPTION_CHANNEL_TYPE_REQ
)

4
electrum/lnwire/peer_wire.csv

@ -53,6 +53,8 @@ msgdata,open_channel,channel_flags,byte,
msgdata,open_channel,tlvs,open_channel_tlvs,
tlvtype,open_channel_tlvs,upfront_shutdown_script,0
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
msgdata,accept_channel,temporary_channel_id,byte,32
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,
tlvtype,accept_channel_tlvs,upfront_shutdown_script,0
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
msgdata,funding_created,temporary_channel_id,byte,32
msgdata,funding_created,funding_txid,sha256,

Can't render this file because it has a wrong number of fields in line 2.

5
electrum/lnworker.py

@ -167,7 +167,7 @@ BASE_FEATURES = LnFeatures(0)\
| LnFeatures.OPTION_STATIC_REMOTEKEY_OPT\
| LnFeatures.VAR_ONION_OPT\
| LnFeatures.PAYMENT_SECRET_OPT\
| LnFeatures.OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT
| LnFeatures.OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT\
# we do not want to receive unrequested gossip (see lnpeer.maybe_save_remote_update)
LNWALLET_FEATURES = BASE_FEATURES\
@ -177,10 +177,11 @@ LNWALLET_FEATURES = BASE_FEATURES\
| LnFeatures.BASIC_MPP_OPT\
| LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT\
| LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_OPT\
| LnFeatures.OPTION_CHANNEL_TYPE_OPT\
LNGOSSIP_FEATURES = BASE_FEATURES\
| LnFeatures.GOSSIP_QUERIES_OPT\
| LnFeatures.GOSSIP_QUERIES_REQ
| LnFeatures.GOSSIP_QUERIES_REQ\
class LNWorker(Logger, NetworkRetryManager[LNPeerAddr]):

1
electrum/tests/test_lnchannel.py

@ -107,6 +107,7 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
'fail_htlc_reasons': {},
'unfulfilled_htlcs': {},
'revocation_store': {},
'channel_type': lnutil.ChannelType.OPTION_STATIC_REMOTEKEY
}
return StoredDict(state, None, [])

1
electrum/tests/test_lnpeer.py

@ -138,6 +138,7 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
self.features |= LnFeatures.VAR_ONION_OPT
self.features |= LnFeatures.PAYMENT_SECRET_OPT
self.features |= LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT
self.features |= LnFeatures.OPTION_CHANNEL_TYPE_OPT
self.pending_payments = defaultdict(asyncio.Future)
for chan in chans:
chan.lnworker = self

14
electrum/tests/test_lnutil.py

@ -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,
get_compressed_pubkey_from_bech32, split_host_port, ConnStringFormatError,
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.transaction import Transaction, PartialTransaction
from electrum.lnworker import LNWallet
@ -890,3 +890,15 @@ class TestLNUtil(ElectrumTestCase):
self.assertEqual(
None,
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)

20
electrum/wallet_db.py

@ -37,7 +37,7 @@ from .invoices import Invoice
from .keystore import bip44_derivation
from .transaction import Transaction, TxOutpoint, tx_from_any, PartialTransaction, PartialTxOutput
from .logging import Logger
from .lnutil import LOCAL, REMOTE, FeeUpdate, UpdateAddHtlc, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKeypair, RevocationStore
from .lnutil import LOCAL, REMOTE, FeeUpdate, UpdateAddHtlc, LocalConfig, RemoteConfig, ChannelType
from .lnutil import ImportedChannelBackupStorage, OnchainChannelBackupStorage
from .lnutil import ChannelConstraints, Outpoint, ShachainElement
from .json_db import StoredDict, JsonDB, locked, modifier
@ -53,7 +53,7 @@ if TYPE_CHECKING:
OLD_SEED_VERSION = 4 # electrum versions < 2.0
NEW_SEED_VERSION = 11 # electrum versions >= 2.0
FINAL_SEED_VERSION = 43 # electrum >= 2.7 will set this to prevent
FINAL_SEED_VERSION = 44 # electrum >= 2.7 will set this to prevent
# old versions from overwriting new format
@ -192,6 +192,7 @@ class WalletDB(JsonDB):
self._convert_version_41()
self._convert_version_42()
self._convert_version_43()
self._convert_version_44()
self.put('seed_version', FINAL_SEED_VERSION) # just to be sure
self._after_upgrade_tasks()
@ -850,6 +851,19 @@ class WalletDB(JsonDB):
self.data['channels'] = channels
self.data['seed_version'] = 43
def _convert_version_44(self):
if not self._is_upgrade_method_needed(43, 43):
return
channels = self.data.get('channels', {})
for key, item in channels.items():
if item['static_remotekey_enabled']:
channel_type = ChannelType.OPTION_STATIC_REMOTEKEY
else:
channel_type = ChannelType(0)
del item['static_remotekey_enabled']
item['channel_type'] = channel_type
self.data['seed_version'] = 44
def _convert_imported(self):
if not self._is_upgrade_method_needed(0, 13):
return
@ -1377,6 +1391,8 @@ class WalletDB(JsonDB):
v = ChannelConstraints(**v)
elif key == 'funding_outpoint':
v = Outpoint(**v)
elif key == 'channel_type':
v = ChannelType(v)
return v
def _should_convert_to_stored_dict(self, key) -> bool:

Loading…
Cancel
Save