Browse Source

public channels:

- send node and channel announcements.
 - store channel_flags in constraints
 - store signatures in local and remote configs
master
ThomasV 2 years ago
parent
commit
98a4d7b60d
  1. 11
      electrum/commands.py
  2. 42
      electrum/lnchannel.py
  3. 171
      electrum/lnpeer.py
  4. 4
      electrum/lnutil.py
  5. 35
      electrum/lnworker.py
  6. 1
      electrum/simple_config.py
  7. 6
      electrum/tests/test_lnchannel.py
  8. 15
      electrum/wallet_db.py

11
electrum/commands.py

@ -1137,11 +1137,15 @@ class Commands:
} for p in lnworker.peers.values()]
@command('wpnl')
async def open_channel(self, connection_string, amount, push_amount=0, password=None, wallet: Abstract_Wallet = None):
async def open_channel(self, connection_string, amount, push_amount=0, public=False, password=None, wallet: Abstract_Wallet = None):
funding_sat = satoshis(amount)
push_sat = satoshis(push_amount)
peer = await wallet.lnworker.add_peer(connection_string)
chan, funding_tx = await wallet.lnworker.open_channel_with_peer(peer, funding_sat, push_sat, password)
chan, funding_tx = await wallet.lnworker.open_channel_with_peer(
peer, funding_sat,
push_sat=push_sat,
public=public,
password=password)
return chan.funding_outpoint.to_str()
@command('')
@ -1460,7 +1464,8 @@ command_options = {
'from_amount': (None, "Amount to convert (default: 1)"),
'from_ccy': (None, "Currency to convert from"),
'to_ccy': (None, "Currency to convert to"),
'unlock': (None, "Unlock the wallet (store the password in memory).")
'unlock': (None, "Unlock the wallet (store the password in memory)."),
'public': (None, 'Channel will be announced'),
}

42
electrum/lnchannel.py

@ -69,6 +69,8 @@ if TYPE_CHECKING:
from .json_db import StoredDict
from .lnrouter import RouteEdge
# channel flags
CF_ANNOUNCE_CHANNEL = 0x01
# lightning channel states
# Note: these states are persisted by name (for a given channel) in the wallet file,
@ -392,6 +394,10 @@ class AbstractChannel(Logger, ABC):
def is_initiator(self) -> bool:
pass
@abstractmethod
def is_public(self) -> bool:
pass
@abstractmethod
def is_funding_tx_mined(self, funding_height: TxMinedInfo) -> bool:
pass
@ -507,11 +513,13 @@ class ChannelBackup(AbstractChannel):
initial_msat=None,
reserve_sat=None,
funding_locked_received=False,
was_announced=False,
current_commitment_signature=None,
current_htlc_signatures=b'',
htlc_minimum_msat=1,
upfront_shutdown_script='')
upfront_shutdown_script='',
announcement_node_sig=b'',
announcement_bitcoin_sig=b'',
)
self.config[REMOTE] = RemoteConfig(
# payment_basepoint needed to deobfuscate ctn in our_ctx
payment_basepoint=OnlyPubkeyKeypair(cb.remote_payment_pubkey),
@ -530,7 +538,10 @@ class ChannelBackup(AbstractChannel):
htlc_minimum_msat=None,
next_per_commitment_point=None,
current_per_commitment_point=None,
upfront_shutdown_script='')
upfront_shutdown_script='',
announcement_node_sig=b'',
announcement_bitcoin_sig=b'',
)
def can_be_deleted(self):
return self.is_imported or self.is_redeemed()
@ -567,6 +578,9 @@ class ChannelBackup(AbstractChannel):
def is_initiator(self):
return self.cb.is_initiator
def is_public(self):
return False
def get_oldest_unrevoked_ctn(self, who):
return -1
@ -647,13 +661,13 @@ class Channel(AbstractChannel):
self.peer_state = PeerState.DISCONNECTED
self._sweep_info = {}
self._outgoing_channel_update = None # type: Optional[bytes]
self._chan_ann_without_sigs = None # type: Optional[bytes]
self.revocation_store = RevocationStore(state["revocation_store"])
self._can_send_ctx_updates = True # type: bool
self._receive_fail_reasons = {} # type: Dict[int, (bytes, OnionRoutingFailure)]
self.should_request_force_close = False
self.unconfirmed_closing_txid = None # not a state, only for GUI
self.sent_channel_ready = False # no need to persist this, because channel_ready is re-sent in channel_reestablish
self.sent_announcement_signatures = False
def get_local_scid_alias(self, *, create_new_if_needed: bool = False) -> Optional[bytes]:
"""Get scid_alias to be used for *outgoing* HTLCs.
@ -688,6 +702,9 @@ class Channel(AbstractChannel):
def get_capacity(self):
return self.constraints.capacity
def is_public(self):
return bool(self.constraints.flags & CF_ANNOUNCE_CHANNEL)
def is_initiator(self):
return self.constraints.is_initiator
@ -785,19 +802,14 @@ class Channel(AbstractChannel):
return chan_upd
def construct_channel_announcement_without_sigs(self) -> bytes:
if self._chan_ann_without_sigs is not None:
return self._chan_ann_without_sigs
if not self.lnworker:
raise Exception('lnworker not set for channel!')
bitcoin_keys = [self.config[REMOTE].multisig_key.pubkey,
self.config[LOCAL].multisig_key.pubkey]
bitcoin_keys = [
self.config[REMOTE].multisig_key.pubkey,
self.config[LOCAL].multisig_key.pubkey]
node_ids = [self.node_id, self.get_local_pubkey()]
sorted_node_ids = list(sorted(node_ids))
if sorted_node_ids != node_ids:
node_ids = sorted_node_ids
bitcoin_keys.reverse()
chan_ann = encode_msg(
"channel_announcement",
len=0,
@ -809,10 +821,12 @@ class Channel(AbstractChannel):
bitcoin_key_1=bitcoin_keys[0],
bitcoin_key_2=bitcoin_keys[1],
)
self._chan_ann_without_sigs = chan_ann
return chan_ann
def get_channel_announcement_hash(self):
chan_ann = self.construct_channel_announcement_without_sigs()
return sha256d(chan_ann[256+2:])
def is_static_remotekey_enabled(self) -> bool:
channel_type = ChannelType(self.storage.get('channel_type'))
return bool(channel_type & ChannelType.OPTION_STATIC_REMOTEKEY)

171
electrum/lnpeer.py

@ -31,7 +31,7 @@ from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_pay
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure,
ProcessedOnionPacket, UnsupportedOnionPacketVersion, InvalidOnionMac, InvalidOnionPubkey,
OnionFailureCodeMetaFlag)
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption, CF_ANNOUNCE_CHANNEL
from . import lnutil
from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, ChannelConfig,
RemoteConfig, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
@ -107,7 +107,6 @@ class Peer(Logger):
self.funding_created_sent = set() # for channels in PREOPENING
self.funding_signed_sent = set() # for channels in PREOPENING
self.shutdown_received = {} # chan_id -> asyncio.Future()
self.announcement_signatures = defaultdict(asyncio.Queue)
self.channel_reestablish_msg = defaultdict(self.asyncio_loop.create_future)
self.orphan_channel_updates = OrderedDict() # type: OrderedDict[ShortChannelID, dict]
Logger.__init__(self)
@ -406,10 +405,17 @@ class Peer(Logger):
self.orphan_channel_updates.popitem(last=False)
def on_announcement_signatures(self, chan: Channel, payload):
if chan.config[LOCAL].was_announced:
h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan)
else:
self.announcement_signatures[chan.channel_id].put_nowait(payload)
h = chan.get_channel_announcement_hash()
node_signature = payload["node_signature"]
bitcoin_signature = payload["bitcoin_signature"]
if not ecc.verify_signature(chan.config[REMOTE].multisig_key.pubkey, bitcoin_signature, h):
raise Exception("bitcoin_sig invalid in announcement_signatures")
if not ecc.verify_signature(self.pubkey, node_signature, h):
raise Exception("node_sig invalid in announcement_signatures")
chan.config[REMOTE].announcement_node_sig = node_signature
chan.config[REMOTE].announcement_bitcoin_sig = bitcoin_signature
self.lnworker.save_channel(chan)
self.maybe_send_announcement_signatures(chan, is_reply=True)
def handle_disconnect(func):
@functools.wraps(func)
@ -434,6 +440,7 @@ class Peer(Logger):
await group.spawn(self.htlc_switch())
await group.spawn(self.query_gossip())
await group.spawn(self.process_gossip())
await group.spawn(self.send_own_gossip())
async def process_gossip(self):
while True:
@ -458,6 +465,20 @@ class Peer(Logger):
if self.network.lngossip:
await self.network.lngossip.process_gossip(chan_anns, node_anns, chan_upds)
async def send_own_gossip(self):
if self.lnworker == self.lnworker.network.lngossip:
return
await asyncio.sleep(10)
while True:
public_channels = [chan for chan in self.lnworker.channels.values() if chan.is_public()]
if public_channels:
alias = self.lnworker.config.LIGHTNING_NODE_ALIAS
self.send_node_announcement(alias)
for chan in public_channels:
if chan.is_open() and chan.peer_state == PeerState.GOOD:
self.send_channel_announcement(chan)
await asyncio.sleep(600)
async def query_gossip(self):
try:
await util.wait_for2(self.initialized, LN_P2P_NETWORK_TIMEOUT)
@ -654,10 +675,11 @@ class Peer(Logger):
initial_msat=initial_msat,
reserve_sat=reserve_sat,
funding_locked_received=False,
was_announced=False,
current_commitment_signature=None,
current_htlc_signatures=b'',
htlc_minimum_msat=1,
announcement_node_sig=b'',
announcement_bitcoin_sig=b'',
)
local_config.validate_params(funding_sat=funding_sat, config=self.network.config, peer_features=self.features)
return local_config
@ -687,6 +709,7 @@ class Peer(Logger):
funding_tx: 'PartialTransaction',
funding_sat: int,
push_msat: int,
public: bool,
temp_channel_id: bytes
) -> Tuple[Channel, 'PartialTransaction']:
"""Implements the channel opening flow.
@ -704,6 +727,10 @@ class Peer(Logger):
if self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.pubkey):
raise Exception('Not a trampoline node: ' + str(self.their_features))
if public and not self.lnworker.config.EXPERIMENTAL_LN_FORWARD_PAYMENTS:
raise Exception('Cannot create public channels')
channel_flags = CF_ANNOUNCE_CHANNEL if public else 0
feerate = self.lnworker.current_feerate_per_kw()
# we set a channel type for internal bookkeeping
open_channel_tlvs = {}
@ -753,7 +780,7 @@ class Peer(Logger):
first_per_commitment_point=per_commitment_point_first,
to_self_delay=local_config.to_self_delay,
max_htlc_value_in_flight_msat=local_config.max_htlc_value_in_flight_msat,
channel_flags=0x00, # not willing to announce channel
channel_flags=channel_flags,
channel_reserve_satoshis=local_config.reserve_sat,
htlc_minimum_msat=local_config.htlc_minimum_msat,
open_channel_tlvs=open_channel_tlvs,
@ -797,6 +824,8 @@ class Peer(Logger):
next_per_commitment_point=remote_per_commitment_point,
current_per_commitment_point=None,
upfront_shutdown_script=upfront_shutdown_script,
announcement_node_sig=b'',
announcement_bitcoin_sig=b'',
)
ChannelConfig.cross_validate_params(
local_config=local_config,
@ -838,6 +867,7 @@ class Peer(Logger):
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_index)
outpoint = Outpoint(funding_txid, funding_index)
constraints = ChannelConstraints(
flags=channel_flags,
capacity=funding_sat,
is_initiator=True,
funding_txn_minimum_depth=funding_txn_minimum_depth
@ -956,6 +986,8 @@ class Peer(Logger):
next_per_commitment_point=payload['first_per_commitment_point'],
current_per_commitment_point=None,
upfront_shutdown_script=upfront_shutdown_script,
announcement_node_sig=b'',
announcement_bitcoin_sig=b'',
)
ChannelConfig.cross_validate_params(
local_config=local_config,
@ -967,9 +999,7 @@ class Peer(Logger):
peer_features=self.features,
)
# note: we ignore payload['channel_flags'], which e.g. contains 'announce_channel'.
# Notably, if the remote sets 'announce_channel' to True, we will ignore that too,
# but we will not play along with actually announcing the channel (so we keep it private).
channel_flags = ord(payload['channel_flags'])
# -> accept channel
# for the first commitment transaction
@ -1018,9 +1048,10 @@ class Peer(Logger):
funding_txid = funding_created['funding_txid'][::-1].hex()
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_idx)
constraints = ChannelConstraints(
flags=channel_flags,
capacity=funding_sat,
is_initiator=False,
funding_txn_minimum_depth=min_depth
funding_txn_minimum_depth=min_depth,
)
outpoint = Outpoint(funding_txid, funding_idx)
chan_dict = self.create_channel_storage(
@ -1277,6 +1308,8 @@ class Peer(Logger):
chan_just_became_ready = (their_next_local_ctn == next_local_ctn == 1)
if chan_just_became_ready or self.features.supports(LnFeatures.OPTION_SCID_ALIAS_OPT):
self.send_channel_ready(chan)
self.maybe_send_announcement_signatures(chan)
# checks done
util.trigger_callback('channel', self.lnworker.wallet, chan)
# if we have sent a previous shutdown, it must be retransmitted (Bolt2)
@ -1319,58 +1352,52 @@ class Peer(Logger):
self.lnworker.save_channel(chan)
self.maybe_mark_open(chan)
def on_network_update(self, chan: Channel, funding_tx_depth: int):
"""
Only called when the channel is OPEN.
Runs on the Network thread.
"""
if not chan.config[LOCAL].was_announced and funding_tx_depth >= 6:
# don't announce our channels
# FIXME should this be a field in chan.local_state maybe?
return
chan.config[LOCAL].was_announced = True
self.lnworker.save_channel(chan)
coro = self.handle_announcements(chan)
asyncio.run_coroutine_threadsafe(coro, self.asyncio_loop)
@log_exceptions
async def handle_announcements(self, chan: Channel):
h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan)
announcement_signatures_msg = await self.announcement_signatures[chan.channel_id].get()
remote_node_sig = announcement_signatures_msg["node_signature"]
remote_bitcoin_sig = announcement_signatures_msg["bitcoin_signature"]
if not ecc.verify_signature(chan.config[REMOTE].multisig_key.pubkey, remote_bitcoin_sig, h):
raise Exception("bitcoin_sig invalid in announcement_signatures")
if not ecc.verify_signature(self.pubkey, remote_node_sig, h):
raise Exception("node_sig invalid in announcement_signatures")
def send_node_announcement(self, alias:str):
timestamp = int(time.time())
node_id = privkey_to_pubkey(self.privkey)
features = self.features.for_node_announcement()
b = int.bit_length(features)
flen = b // 8 + int(bool(b % 8))
rgb_color = bytes.fromhex('000000')
alias = bytes(alias, 'utf8')
alias += bytes(32 - len(alias))
addresses = b''
raw_msg = encode_msg(
"node_announcement",
flen=flen,
features=features,
timestamp=timestamp,
rgb_color=rgb_color,
node_id=node_id,
alias=alias,
addrlen=len(addresses),
addresses=addresses)
h = sha256d(raw_msg[64+2:])
signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s)
message_type, payload = decode_msg(raw_msg)
payload['signature'] = signature
raw_msg = encode_msg(message_type, **payload)
self.transport.send_bytes(raw_msg)
node_sigs = [remote_node_sig, local_node_sig]
bitcoin_sigs = [remote_bitcoin_sig, local_bitcoin_sig]
def send_channel_announcement(self, chan: Channel):
node_ids = [chan.node_id, chan.get_local_pubkey()]
node_sigs = [chan.config[REMOTE].announcement_node_sig, chan.config[LOCAL].announcement_node_sig]
bitcoin_sigs = [chan.config[REMOTE].announcement_bitcoin_sig, chan.config[LOCAL].announcement_bitcoin_sig]
bitcoin_keys = [chan.config[REMOTE].multisig_key.pubkey, chan.config[LOCAL].multisig_key.pubkey]
if self.node_ids[0] > self.node_ids[1]:
sorted_node_ids = list(sorted(node_ids))
if sorted_node_ids != node_ids:
node_sigs.reverse()
bitcoin_sigs.reverse()
node_ids = list(reversed(self.node_ids))
node_ids.reverse()
bitcoin_keys.reverse()
else:
node_ids = self.node_ids
self.send_message("channel_announcement",
node_signatures_1=node_sigs[0],
node_signatures_2=node_sigs[1],
bitcoin_signature_1=bitcoin_sigs[0],
bitcoin_signature_2=bitcoin_sigs[1],
len=0,
#features not set (defaults to zeros)
chain_hash=constants.net.rev_genesis_bytes(),
short_channel_id=chan.short_channel_id,
node_id_1=node_ids[0],
node_id_2=node_ids[1],
bitcoin_key_1=bitcoin_keys[0],
bitcoin_key_2=bitcoin_keys[1]
)
raw_msg = chan.construct_channel_announcement_without_sigs()
message_type, payload = decode_msg(raw_msg)
payload['node_signature_1'] = node_sigs[0]
payload['node_signature_2'] = node_sigs[1]
payload['bitcoin_signature_1'] = bitcoin_sigs[0]
payload['bitcoin_signature_2'] = bitcoin_sigs[1]
raw_msg = encode_msg(message_type, **payload)
self.transport.send_bytes(raw_msg)
def maybe_mark_open(self, chan: Channel):
if not chan.sent_channel_ready:
@ -1404,19 +1431,27 @@ class Peer(Logger):
chan_upd = chan.get_outgoing_gossip_channel_update()
self.transport.send_bytes(chan_upd)
def send_announcement_signatures(self, chan: Channel):
chan_ann = chan.construct_channel_announcement_without_sigs()
preimage = chan_ann[256+2:]
msg_hash = sha256d(preimage)
bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(msg_hash, sig_string_from_r_and_s)
node_signature = ecc.ECPrivkey(self.privkey).sign(msg_hash, sig_string_from_r_and_s)
self.send_message("announcement_signatures",
def maybe_send_announcement_signatures(self, chan: Channel, is_reply=False):
if not chan.is_public():
return
if chan.sent_announcement_signatures:
return
if not is_reply and chan.config[REMOTE].announcement_node_sig:
return
h = chan.get_channel_announcement_hash()
bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s)
node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s)
self.send_message(
"announcement_signatures",
channel_id=chan.channel_id,
short_channel_id=chan.short_channel_id,
node_signature=node_signature,
bitcoin_signature=bitcoin_signature
)
return msg_hash, node_signature, bitcoin_signature
chan.config[LOCAL].announcement_node_sig = node_signature
chan.config[LOCAL].announcement_bitcoin_sig = bitcoin_signature
self.lnworker.save_channel(chan)
chan.sent_announcement_signatures = True
def on_update_fail_htlc(self, chan: Channel, payload):
htlc_id = payload["id"]
@ -1982,7 +2017,7 @@ class Peer(Logger):
feerate = payload["feerate_per_kw"]
chan.update_fee(feerate, False)
async def maybe_update_fee(self, chan: Channel):
def maybe_update_fee(self, chan: Channel):
"""
called when our fee estimates change
"""

4
electrum/lnutil.py

@ -90,6 +90,8 @@ class ChannelConfig(StoredObject):
reserve_sat = attr.ib(type=int) # applies to OTHER ctx
htlc_minimum_msat = attr.ib(type=int) # smallest value for INCOMING htlc
upfront_shutdown_script = attr.ib(type=bytes, converter=hex_to_bytes)
announcement_node_sig = attr.ib(type=bytes, converter=hex_to_bytes)
announcement_bitcoin_sig = attr.ib(type=bytes, converter=hex_to_bytes)
def validate_params(self, *, funding_sat: int, config: 'SimpleConfig', peer_features: 'LnFeatures') -> None:
conf_name = type(self).__name__
@ -193,7 +195,6 @@ class ChannelConfig(StoredObject):
class LocalConfig(ChannelConfig):
channel_seed = attr.ib(type=bytes, converter=hex_to_bytes) # type: Optional[bytes]
funding_locked_received = attr.ib(type=bool)
was_announced = attr.ib(type=bool)
current_commitment_signature = attr.ib(type=bytes, converter=hex_to_bytes)
current_htlc_signatures = attr.ib(type=bytes, converter=hex_to_bytes)
per_commitment_secret_seed = attr.ib(type=bytes, converter=hex_to_bytes)
@ -242,6 +243,7 @@ class FeeUpdate(StoredObject):
@stored_as('constraints')
@attr.s
class ChannelConstraints(StoredObject):
flags = attr.ib(type=int, converter=int)
capacity = attr.ib(type=int) # in sat
is_initiator = attr.ib(type=bool) # note: sometimes also called "funder"
funding_txn_minimum_depth = attr.ib(type=int)

35
electrum/lnworker.py

@ -1204,10 +1204,9 @@ class LNWallet(LNWorker):
elif chan.get_state() == ChannelState.OPEN:
peer = self._peers.get(chan.node_id)
if peer:
await peer.maybe_update_fee(chan)
conf = self.lnwatcher.adb.get_tx_height(chan.funding_outpoint.txid).conf
peer.on_network_update(chan, conf)
if peer and peer.is_initialized() and chan.peer_state == PeerState.GOOD:
peer.maybe_update_fee(chan)
peer.maybe_send_announcement_signatures(chan)
elif chan.get_state() == ChannelState.FORCE_CLOSING:
force_close_tx = chan.force_close_tx()
@ -1218,7 +1217,11 @@ class LNWallet(LNWorker):
await self.network.try_broadcasting(force_close_tx, 'force-close')
@log_exceptions
async def open_channel_with_peer(self, peer, funding_sat, push_sat, password):
async def open_channel_with_peer(
self, peer, funding_sat, *,
push_sat: int = 0,
public: bool = False,
password=None):
coins = self.wallet.get_spendable_coins(None)
node_id = peer.pubkey
funding_tx = self.mktx_for_open_channel(
@ -1231,6 +1234,7 @@ class LNWallet(LNWorker):
funding_tx=funding_tx,
funding_sat=funding_sat,
push_sat=push_sat,
public=public,
password=password)
return chan, funding_tx
@ -1241,12 +1245,14 @@ class LNWallet(LNWorker):
funding_tx: PartialTransaction,
funding_sat: int,
push_sat: int,
public: bool,
password: Optional[str]) -> Tuple[Channel, PartialTransaction]:
coro = peer.channel_establishment_flow(
funding_tx=funding_tx,
funding_sat=funding_sat,
push_msat=push_sat * 1000,
public=public,
temp_channel_id=os.urandom(32))
chan, funding_tx = await util.wait_for2(coro, LN_P2P_NETWORK_TIMEOUT)
util.trigger_callback('channels_updated', self.wallet)
@ -1329,8 +1335,15 @@ class LNWallet(LNWorker):
pass
return funding_sat, min_funding_sat
def open_channel(self, *, connect_str: str, funding_tx: PartialTransaction,
funding_sat: int, push_amt_sat: int, password: str = None) -> Tuple[Channel, PartialTransaction]:
def open_channel(
self, *,
connect_str: str,
funding_tx: PartialTransaction,
funding_sat: int,
push_amt_sat: int,
public: bool = False,
password: str = None) -> Tuple[Channel, PartialTransaction]:
if funding_sat > self.config.LIGHTNING_MAX_FUNDING_SAT:
raise Exception(_("Requested channel capacity is over maximum."))
@ -1340,8 +1353,12 @@ class LNWallet(LNWorker):
except concurrent.futures.TimeoutError:
raise Exception(_("add peer timed out"))
coro = self._open_channel_coroutine(
peer=peer, funding_tx=funding_tx, funding_sat=funding_sat,
push_sat=push_amt_sat, password=password)
peer=peer,
funding_tx=funding_tx,
funding_sat=funding_sat,
push_sat=push_amt_sat,
public=public,
password=password)
fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
try:
chan, funding_tx = fut.result()

1
electrum/simple_config.py

@ -1024,6 +1024,7 @@ This will result in longer routes; it might increase your fees and decrease the
)
INITIAL_TRAMPOLINE_FEE_LEVEL = ConfigVar('initial_trampoline_fee_level', default=1, type_=int)
LIGHTNING_NODE_ALIAS = ConfigVar('lightning_node_alias', default='', type_=str)
EXPERIMENTAL_LN_FORWARD_PAYMENTS = ConfigVar('lightning_forward_payments', default=False, type_=bool)
EXPERIMENTAL_LN_FORWARD_TRAMPOLINE_PAYMENTS = ConfigVar('lightning_forward_trampoline_payments', default=False, type_=bool)
TEST_FAIL_HTLCS_WITH_TEMP_NODE_FAILURE = ConfigVar('test_fail_htlcs_with_temp_node_failure', default=False, type_=bool)

6
electrum/tests/test_lnchannel.py

@ -72,6 +72,8 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
next_per_commitment_point=nex,
current_per_commitment_point=cur,
upfront_shutdown_script=b'',
announcement_node_sig=b'',
announcement_bitcoin_sig=b'',
),
"local_config":lnpeer.LocalConfig(
channel_seed = None,
@ -88,13 +90,15 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
reserve_sat=0,
per_commitment_secret_seed=seed,
funding_locked_received=True,
was_announced=False,
current_commitment_signature=None,
current_htlc_signatures=None,
htlc_minimum_msat=1,
upfront_shutdown_script=b'',
announcement_node_sig=b'',
announcement_bitcoin_sig=b'',
),
"constraints":lnpeer.ChannelConstraints(
flags=0,
capacity=funding_sat,
is_initiator=is_initiator,
funding_txn_minimum_depth=3,

15
electrum/wallet_db.py

@ -67,7 +67,7 @@ class WalletUnfinished(WalletFileException):
# seed_version is now used for the version of the wallet file
OLD_SEED_VERSION = 4 # electrum versions < 2.0
NEW_SEED_VERSION = 11 # electrum versions >= 2.0
FINAL_SEED_VERSION = 55 # electrum >= 2.7 will set this to prevent
FINAL_SEED_VERSION = 56 # electrum >= 2.7 will set this to prevent
# old versions from overwriting new format
@ -225,6 +225,7 @@ class WalletDBUpgrader(Logger):
self._convert_version_53()
self._convert_version_54()
self._convert_version_55()
self._convert_version_56()
self.put('seed_version', FINAL_SEED_VERSION) # just to be sure
def _convert_wallet_type(self):
@ -1082,6 +1083,18 @@ class WalletDBUpgrader(Logger):
self.data[key[:-1]] = self.data.pop(key)
self.data['seed_version'] = 55
def _convert_version_56(self):
if not self._is_upgrade_method_needed(55, 55):
return
channels = self.data.get('channels', {})
for key, item in channels.items():
item['constraints']['flags'] = 0
for c in ['local_config', 'remote_config']:
item[c]['announcement_node_sig'] = ''
item[c]['announcement_bitcoin_sig'] = ''
item['local_config'].pop('was_announced')
self.data['seed_version'] = 56
def _convert_imported(self):
if not self._is_upgrade_method_needed(0, 13):
return

Loading…
Cancel
Save