URI: 
       tlnpeer: send own outgoing channel updates to remote peer - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 47ee02569af047d803592c43277ffb053a547bd7
   DIR parent f0588846d51c5b206ccc00121c4fbb0030708cce
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Fri, 16 Aug 2019 22:35:25 +0200
       
       lnpeer: send own outgoing channel updates to remote peer
       
       Diffstat:
         M electrum/lnchannel.py               |       3 ++-
         M electrum/lnpeer.py                  |      72 ++++++++++++++++++++-----------
       
       2 files changed, 48 insertions(+), 27 deletions(-)
       ---
   DIR diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py
       t@@ -129,7 +129,7 @@ class Channel(Logger):
                self.channel_id = bfh(state["channel_id"]) if type(state["channel_id"]) not in (bytes, type(None)) else state["channel_id"]
                self.constraints = ChannelConstraints(**state["constraints"]) if type(state["constraints"]) is not ChannelConstraints else state["constraints"]
                self.funding_outpoint = Outpoint(**dict(decodeAll(state["funding_outpoint"], False))) if type(state["funding_outpoint"]) is not Outpoint else state["funding_outpoint"]
       -        self.node_id = bfh(state["node_id"]) if type(state["node_id"]) not in (bytes, type(None)) else state["node_id"]
       +        self.node_id = bfh(state["node_id"]) if type(state["node_id"]) not in (bytes, type(None)) else state["node_id"]  # type: bytes
                self.short_channel_id = bfh(state["short_channel_id"]) if type(state["short_channel_id"]) not in (bytes, type(None)) else state["short_channel_id"]
                self.short_channel_id_predicted = self.short_channel_id
                self.onion_keys = str_bytes_dict_from_save(state.get('onion_keys', {}))
       t@@ -145,6 +145,7 @@ class Channel(Logger):
                self._state = None
                self.set_state('DISCONNECTED')
                self.sweep_info = {}
       +        self._outgoing_channel_update = None  # type: Optional[bytes]
        
            def get_feerate(self, subject, ctn):
                return self.hm.get_feerate(subject, ctn)
   DIR diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
       t@@ -63,9 +63,9 @@ class Peer(Logger):
                self.initialized = asyncio.Event()
                self.querying = asyncio.Event()
                self.transport = transport
       -        self.pubkey = pubkey
       +        self.pubkey = pubkey  # remote pubkey
                self.lnworker = lnworker
       -        self.privkey = lnworker.node_keypair.privkey
       +        self.privkey = lnworker.node_keypair.privkey  # local privkey
                self.localfeatures = self.lnworker.localfeatures
                self.node_ids = [self.pubkey, privkey_to_pubkey(self.privkey)]
                self.network = lnworker.network
       t@@ -938,6 +938,7 @@ class Peer(Logger):
        
            def mark_open(self, chan: Channel):
                assert chan.short_channel_id is not None
       +        scid = format_short_channel_id(chan.short_channel_id)
                # only allow state transition to "OPEN" from "OPENING"
                if chan.get_state() != "OPENING":
                    return
       t@@ -945,17 +946,21 @@ class Peer(Logger):
                chan.set_state("OPEN")
                self.network.trigger_callback('channel', chan)
                asyncio.ensure_future(self.add_own_channel(chan))
       -        self.logger.info("CHANNEL OPENING COMPLETED")
       +        self.logger.info(f"CHANNEL OPENING COMPLETED for {scid}")
       +        forwarding_enabled = self.network.config.get('lightning_forward_payments', False)
       +        if forwarding_enabled:
       +            # send channel_update of outgoing edge to peer,
       +            # so that channel can be used to to receive payments
       +            self.logger.info(f"sending channel update for outgoing edge of {scid}")
       +            chan_upd = self.get_outgoing_gossip_channel_update_for_chan(chan)
       +            self.transport.send_bytes(chan_upd)
        
            async def add_own_channel(self, chan):
                # add channel to database
                bitcoin_keys = [chan.config[LOCAL].multisig_key.pubkey, chan.config[REMOTE].multisig_key.pubkey]
                sorted_node_ids = list(sorted(self.node_ids))
                if sorted_node_ids != self.node_ids:
       -            node_ids = sorted_node_ids
                    bitcoin_keys.reverse()
       -        else:
       -            node_ids = self.node_ids
                # note: we inject a channel announcement, and a channel update (for outgoing direction)
                # This is atm needed for
                # - finding routes
       t@@ -966,8 +971,8 @@ class Peer(Logger):
                self.channel_db.add_channel_announcement(
                    {
                        "short_channel_id": chan.short_channel_id,
       -                "node_id_1": node_ids[0],
       -                "node_id_2": node_ids[1],
       +                "node_id_1": sorted_node_ids[0],
       +                "node_id_2": sorted_node_ids[1],
                        'chain_hash': constants.net.rev_genesis_bytes(),
                        'len': b'\x00\x00',
                        'features': b'',
       t@@ -976,29 +981,44 @@ class Peer(Logger):
                    },
                    trusted=True)
                # only inject outgoing direction:
       -        channel_flags = b'\x00' if node_ids[0] == privkey_to_pubkey(self.privkey) else b'\x01'
       -        now = int(time.time())
       -        self.channel_db.add_channel_update(
       -            {
       -                "short_channel_id": chan.short_channel_id,
       -                'channel_flags': channel_flags,
       -                'message_flags': b'\x00',
       -                'cltv_expiry_delta': b'\x90',
       -                'htlc_minimum_msat': b'\x03\xe8',
       -                'fee_base_msat': b'\x03\xe8',
       -                'fee_proportional_millionths': b'\x01',
       -                'chain_hash': constants.net.rev_genesis_bytes(),
       -                'timestamp': now.to_bytes(4, byteorder="big")
       -            })
       +        chan_upd_bytes = self.get_outgoing_gossip_channel_update_for_chan(chan)
       +        chan_upd_payload = decode_msg(chan_upd_bytes)[1]
       +        self.channel_db.add_channel_update(chan_upd_payload)
                # peer may have sent us a channel update for the incoming direction previously
       -        # note: if we were offline when the 3rd conf happened, lnd will never send us this channel_update
       -        # see https://github.com/lightningnetwork/lnd/issues/1347
       -        #self.send_message("query_short_channel_ids", chain_hash=constants.net.rev_genesis_bytes(),
       -        #                          len=9, encoded_short_ids=b'\x00'+chan.short_channel_id)
                pending_channel_update = self.orphan_channel_updates.get(chan.short_channel_id)
                if pending_channel_update:
                    self.channel_db.add_channel_update(pending_channel_update)
        
       +    def get_outgoing_gossip_channel_update_for_chan(self, chan: Channel) -> bytes:
       +        if chan._outgoing_channel_update is not None:
       +            return chan._outgoing_channel_update
       +        sorted_node_ids = list(sorted(self.node_ids))
       +        channel_flags = b'\x00' if sorted_node_ids[0] == privkey_to_pubkey(self.privkey) else b'\x01'
       +        now = int(time.time())
       +        htlc_maximum_msat = min(chan.config[REMOTE].max_htlc_value_in_flight_msat, 1000 * chan.constraints.capacity)
       +
       +        chan_upd = encode_msg(
       +            "channel_update",
       +            short_channel_id=chan.short_channel_id,
       +            channel_flags=channel_flags,
       +            message_flags=b'\x01',
       +            cltv_expiry_delta=lnutil.NBLOCK_OUR_CLTV_EXPIRY_DELTA.to_bytes(2, byteorder="big"),
       +            htlc_minimum_msat=chan.config[REMOTE].htlc_minimum_msat.to_bytes(8, byteorder="big"),
       +            htlc_maximum_msat=htlc_maximum_msat.to_bytes(8, byteorder="big"),
       +            fee_base_msat=lnutil.OUR_FEE_BASE_MSAT.to_bytes(4, byteorder="big"),
       +            fee_proportional_millionths=lnutil.OUR_FEE_PROPORTIONAL_MILLIONTHS.to_bytes(4, byteorder="big"),
       +            chain_hash=constants.net.rev_genesis_bytes(),
       +            timestamp=now.to_bytes(4, byteorder="big"),
       +        )
       +        sighash = sha256d(chan_upd[2 + 64:])
       +        sig = ecc.ECPrivkey(self.privkey).sign(sighash, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
       +        message_type, payload = decode_msg(chan_upd)
       +        payload['signature'] = sig
       +        chan_upd = encode_msg(message_type, **payload)
       +
       +        chan._outgoing_channel_update = chan_upd
       +        return chan_upd
       +
            def send_announcement_signatures(self, chan: Channel):
        
                bitcoin_keys = [chan.config[REMOTE].multisig_key.pubkey,