URI: 
       tln: channel announcements - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit a10676046905818aad14bb829969dc8ed894671a
   DIR parent 275f1e6cbc836c55a77de18143166781406244af
  HTML Author: Janus <ysangkok@gmail.com>
       Date:   Mon, 18 Jun 2018 15:34:18 +0200
       
       ln: channel announcements
       
       Diffstat:
         M lib/lnbase.py                       |      91 ++++++++++++++++++++++++++++++-
         M lib/lnworker.py                     |      26 ++++++++++++--------------
       
       2 files changed, 100 insertions(+), 17 deletions(-)
       ---
   DIR diff --git a/lib/lnbase.py b/lib/lnbase.py
       t@@ -4,15 +4,13 @@
          Derived from https://gist.github.com/AdamISZ/046d05c156aaeb56cc897f85eecb3eb8
        """
        
       -from ecdsa.util import sigdecode_der, sigencode_string_canonize
       -from ecdsa import VerifyingKey
       +from ecdsa.util import sigdecode_der, sigencode_string_canonize, sigdecode_string
        from ecdsa.curves import SECP256k1
        import queue
        import traceback
        import json
        from collections import OrderedDict, defaultdict
        import asyncio
       -import sys
        import os
        import time
        import binascii
       t@@ -599,6 +597,8 @@ class Peer(PrintError):
                self.revoke_and_ack = defaultdict(asyncio.Queue)
                self.update_fulfill_htlc = defaultdict(asyncio.Queue)
                self.commitment_signed = defaultdict(asyncio.Queue)
       +        self.announcement_signatures = defaultdict(asyncio.Queue)
       +        self.is_funding_six_deep = defaultdict(lambda: False)
                self.localfeatures = (0x08 if request_initial_sync else 0)
                self.nodes = {}
                self.channels = lnworker.channels
       t@@ -766,6 +766,10 @@ class Peer(PrintError):
                self.channel_db.on_channel_announcement(payload)
                self.channel_update_event.set()
        
       +    def on_announcement_signatures(self, payload):
       +        channel_id = payload['channel_id']
       +        self.announcement_signatures[channel_id].put_nowait(payload)
       +
            @aiosafe
            async def main_loop(self):
                self.reader, self.writer = await asyncio.open_connection(self.host, self.port)
       t@@ -835,6 +839,7 @@ class Peer(PrintError):
                    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=0x01, # publicly announcing channel
                    channel_reserve_satoshis=10
                )
                self.send_message(msg)
       t@@ -968,6 +973,49 @@ class Peer(PrintError):
                if chan.short_channel_id:
                    self.mark_open(chan)
        
       +    async def funding_six_deep(self, chan):
       +        if self.is_funding_six_deep[chan.channel_id]:
       +            return
       +        self.is_funding_six_deep[chan.channel_id] = True
       +        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.remote_config.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")
       +
       +        node_sigs = [local_node_sig, remote_node_sig]
       +        bitcoin_sigs = [local_bitcoin_sig, remote_bitcoin_sig]
       +        node_ids = [privkey_to_pubkey(self.privkey), self.pubkey]
       +        bitcoin_keys = [chan.local_config.multisig_key.pubkey, chan.remote_config.multisig_key.pubkey]
       +
       +        if node_ids[0] > node_ids[1]:
       +            node_sigs.reverse()
       +            bitcoin_sigs.reverse()
       +            node_ids.reverse()
       +            bitcoin_keys.reverse()
       +
       +        channel_announcement = gen_msg("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
       +            chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)),
       +            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]
       +        )
       +
       +        self.send_message(channel_announcement)
       +
       +        print("SENT CHANNEL ANNOUNCEMENT")
       +
            def mark_open(self, chan):
                if self.channel_state[chan.channel_id] == "OPEN":
                    return
       t@@ -979,8 +1027,45 @@ class Peer(PrintError):
                self.channel_db.on_channel_announcement({"short_channel_id": chan.short_channel_id, "node_id_1": sorted_keys[0], "node_id_2": sorted_keys[1]})
                self.channel_db.on_channel_update({"short_channel_id": chan.short_channel_id, 'flags': b'\x01', 'cltv_expiry_delta': b'\x90', 'htlc_minimum_msat': b'\x03\xe8', 'fee_base_msat': b'\x03\xe8', 'fee_proportional_millionths': b'\x01'})
                self.channel_db.on_channel_update({"short_channel_id": chan.short_channel_id, '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'})
       +
                self.print_error("CHANNEL OPENING COMPLETED")
        
       +    def send_announcement_signatures(self, chan):
       +
       +        bitcoin_keys = [chan.local_config.multisig_key.pubkey,
       +                        chan.remote_config.multisig_key.pubkey]
       +
       +        node_ids = [privkey_to_pubkey(self.privkey),
       +                    self.pubkey]
       +
       +        sorted_node_ids = list(sorted(node_ids))
       +        if sorted_node_ids != node_ids:
       +            node_ids = sorted_node_ids
       +            bitcoin_keys.reverse()
       +
       +        chan_ann = gen_msg("channel_announcement",
       +            len=0,
       +            #features
       +            chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)),
       +            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]
       +        )
       +        to_hash = chan_ann[256+2:]
       +        h = bitcoin.Hash(to_hash)
       +        bitcoin_signature = ecc.ECPrivkey(chan.local_config.multisig_key.privkey).sign(h, sigencode_string_canonize, sigdecode_string)
       +        node_signature = ecc.ECPrivkey(self.privkey).sign(h, sigencode_string_canonize, sigdecode_string)
       +        self.send_message(gen_msg("announcement_signatures",
       +            channel_id=chan.channel_id,
       +            short_channel_id=chan.short_channel_id,
       +            node_signature=node_signature,
       +            bitcoin_signature=bitcoin_signature
       +        ))
       +
       +        return h, node_signature, bitcoin_signature
       +
            def on_update_fail_htlc(self, payload):
                print("UPDATE_FAIL_HTLC", decode_onion_error(payload["reason"], self.node_keys, self.secret_key))
        
   DIR diff --git a/lib/lnworker.py b/lib/lnworker.py
       t@@ -1,13 +1,8 @@
       -import traceback
       -import sys
        import json
        import binascii
        import asyncio
       -import time
        import os
        from decimal import Decimal
       -import binascii
       -import asyncio
        import threading
        from collections import defaultdict
        
       t@@ -15,11 +10,7 @@ from . import constants
        from .bitcoin import sha256, COIN
        from .util import bh2u, bfh, PrintError
        from .constants import set_testnet, set_simnet
       -from .simple_config import SimpleConfig
       -from .network import Network
       -from .storage import WalletStorage
       -from .wallet import Wallet
       -from .lnbase import Peer, Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, OpenChannel, ChannelConstraints, RevocationStore, aiosafe, calc_short_channel_id, privkey_to_pubkey
       +from .lnbase import Peer, Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, OpenChannel, ChannelConstraints, RevocationStore, calc_short_channel_id, privkey_to_pubkey
        from .lightning_payencode.lnaddr import lnencode, LnAddr, lndecode
        from . import lnrouter
        from .ecc import ECPrivkey
       t@@ -142,7 +133,7 @@ class LNWorker(PrintError):
        
                If the Funding TX has not been mined, return None
                """
       -        assert self.channel_state[chan.channel_id] == "OPENING"
       +        assert self.channel_state[chan.channel_id] in ["OPEN", "OPENING"]
                peer = self.peers[chan.node_id]
                conf = self.wallet.get_tx_height(chan.funding_outpoint.txid)[1]
                if conf >= chan.constraints.funding_txn_minimum_depth:
       t@@ -153,14 +144,21 @@ class LNWorker(PrintError):
                        return None
                    chan = chan._replace(short_channel_id = calc_short_channel_id(block_height, tx_pos, chan.funding_outpoint.output_index))
                    self.save_channel(chan)
       -            return chan
       -        return None
       +            return chan, conf
       +        return None, None
        
            def on_network_update(self, event, *args):
                for chan in self.channels.values():
       +            if self.channel_state[chan.channel_id] == "OPEN":
       +                conf = self.wallet.get_tx_height(chan.funding_outpoint.txid)[1]
       +                if conf >= 6:
       +                    peer = self.peers[chan.node_id]
       +                    coro = peer.funding_six_deep(chan)
       +                    fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
       +                    fut.result()
                    if self.channel_state[chan.channel_id] != "OPENING":
                        continue
       -            chan = self.save_short_chan_id(chan)
       +            chan, conf = self.save_short_chan_id(chan)
                    if not chan:
                        self.print_error("network update but funding tx is still not at sufficient depth")
                        continue