diff --git a/electrum/commands.py b/electrum/commands.py index b52b6bdd2..aab0793f2 100644 --- a/electrum/commands.py +++ b/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'), } diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py index 4e2bbb7ee..a342e9c4e 100644 --- a/electrum/lnchannel.py +++ b/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) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index 2d84fdcc1..89edfd11f 100644 --- a/electrum/lnpeer.py +++ b/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 """ diff --git a/electrum/lnutil.py b/electrum/lnutil.py index 5bc6e1f0e..5b8189c2b 100644 --- a/electrum/lnutil.py +++ b/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) diff --git a/electrum/lnworker.py b/electrum/lnworker.py index ede719d2c..3f34eb99b 100644 --- a/electrum/lnworker.py +++ b/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() diff --git a/electrum/simple_config.py b/electrum/simple_config.py index 83020665b..0d9400604 100644 --- a/electrum/simple_config.py +++ b/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) diff --git a/electrum/tests/test_lnchannel.py b/electrum/tests/test_lnchannel.py index db77278d8..76cffd5f8 100644 --- a/electrum/tests/test_lnchannel.py +++ b/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, diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py index 80f27f34d..d441e9c85 100644 --- a/electrum/wallet_db.py +++ b/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