URI: 
       tlnbase: more parts of channel establishment - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 668c3887d6ead44e12a559b49d161258104a3296
   DIR parent 235341544531293cbfde177f1036b57234f62eb6
  HTML Author: Janus <ysangkok@gmail.com>
       Date:   Thu, 12 Apr 2018 19:09:27 +0200
       
       lnbase: more parts of channel establishment
       
       Diffstat:
         M lib/lnbase.py                       |      64 ++++++++++++++++++++++++-------
       
       1 file changed, 50 insertions(+), 14 deletions(-)
       ---
   DIR diff --git a/lib/lnbase.py b/lib/lnbase.py
       t@@ -4,6 +4,8 @@
          Derived from https://gist.github.com/AdamISZ/046d05c156aaeb56cc897f85eecb3eb8
        """
        
       +from ecdsa.util import sigdecode_der, sigencode_string_canonize
       +from ecdsa.curves import SECP256k1
        import subprocess
        import queue
        import traceback
       t@@ -23,6 +25,7 @@ from .bitcoin import public_key_from_private_key, ser_to_point, point_to_ser, st
        from . import bitcoin
        from .constants import set_testnet, set_simnet
        from . import constants
       +from . import transaction
        from .util import PrintError
        from .wallet import Wallet
        from .storage import WalletStorage
       t@@ -117,10 +120,14 @@ def gen_msg(msg_type, **kwargs):
                except KeyError:
                    param = 0
                try:
       -            if not isinstance(param, bytes): param = param.to_bytes(length=leng, byteorder="big")
       +            if not isinstance(param, bytes):
       +                assert isinstance(param, int), "field {} is neither bytes or int".format(k)
       +                param = param.to_bytes(length=leng, byteorder="big")
                except ValueError:
                    raise Exception("{} does not fit in {} bytes".format(k, leng))
                lengths[k] = len(param)
       +        if lengths[k] != leng:
       +            raise Exception("field {} is {} bytes long, should be {} bytes long".format(k, lengths[k], leng))
                data += param
            return data
        
       t@@ -218,12 +225,14 @@ def create_ephemeral_key(privkey):
            pub = privkey_to_pubkey(privkey)
            return (privkey[:32], pub)
        
       -def get_unused_public_keys():
       +def get_unused_keys():
            xprv, xpub = bitcoin.bip32_root(b"testseed", "p2wpkh")
            for i in itertools.count():
       -        childxpub = bitcoin.bip32_public_derivation(xpub, "m/", "m/42/"+str(i))
       +        childxprv, childxpub = bitcoin.bip32_private_derivation(xprv, "m/", "m/42/"+str(i))
                _, _, _, _, child_c, child_cK = bitcoin.deserialize_xpub(childxpub)
       -        yield child_cK
       +        _, _, _, _, _, k = bitcoin.deserialize_xprv(childxprv)
       +        assert len(k) == 32
       +        yield child_cK, k
        
        def aiosafe(f):
            async def f2(*args, **kwargs):
       t@@ -248,6 +257,7 @@ class Peer(PrintError):
                self.read_buffer = b''
                self.ping_time = 0
                self.temporary_channel_id_to_incoming_accept_channel = {}
       +        self.temporary_channel_id_to_incoming_funding_signed = {}
                self.init_message_received_future = asyncio.Future()
        
            def diagnostic_name(self):
       t@@ -326,7 +336,10 @@ class Peer(PrintError):
                f(payload)
        
            def on_error(self, payload):
       -        self.temporary_channel_id_to_incoming_accept_channel[payload["channel_id"]].set_exception(LightningError(payload["data"]))
       +        if payload["channel_id"] in self.temporary_channel_id_to_incoming_accept_channel:
       +            self.temporary_channel_id_to_incoming_accept_channel[payload["channel_id"]].set_exception(LightningError(payload["data"]))
       +        if payload["channel_id"] in self.temporary_channel_id_to_incoming_funding_signed:
       +            self.temporary_channel_id_to_incoming_funding_signed[payload["channel_id"]].set_exception(LightningError(payload["data"]))
        
            def on_ping(self, payload):
                l = int.from_bytes(payload['num_pong_bytes'], byteorder="big")
       t@@ -341,6 +354,9 @@ class Peer(PrintError):
                tx = self.channels[channel_id]
                self.network.broadcast(tx)
        
       +    def on_funding_signed(self, payload):
       +        self.temporary_channel_id_to_incoming_funding_signed[payload["temporary_channel_id"]].set_result(payload)
       +
            def on_funding_locked(self, payload):
                pass
        
       t@@ -367,19 +383,39 @@ class Peer(PrintError):
                self.print_error('closing lnbase')
                self.writer.close()
        
       -    async def channel_establishment_flow(self, wallet):
       +    async def channel_establishment_flow(self, wallet, config):
                await self.init_message_received_future
       -        pubkeys = get_unused_public_keys()
       +        keys = get_unused_keys()
                temp_channel_id = os.urandom(32)
       -        msg = gen_msg("open_channel", temporary_channel_id=temp_channel_id, chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)), funding_satoshis=20000, max_accepted_htlcs=5, funding_pubkey=next(pubkeys), revocation_basepoint=next(pubkeys), htlc_basepoint=next(pubkeys), payment_basepoint=next(pubkeys), delayed_payment_basepoint=next(pubkeys), first_per_commitment_point=next(pubkeys))
       +        funding_pubkey, funding_privkey = next(keys)
       +        msg = gen_msg("open_channel", temporary_channel_id=temp_channel_id, chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)), funding_satoshis=20000, max_accepted_htlcs=5, funding_pubkey=funding_pubkey, revocation_basepoint=next(keys)[0], htlc_basepoint=next(keys)[0], payment_basepoint=next(keys)[0], delayed_payment_basepoint=next(keys)[0], first_per_commitment_point=next(keys)[0])
                self.temporary_channel_id_to_incoming_accept_channel[temp_channel_id] = asyncio.Future()
                self.send_message(msg)
                try:
                    accept_channel = await self.temporary_channel_id_to_incoming_accept_channel[temp_channel_id]
                finally:
                    del self.temporary_channel_id_to_incoming_accept_channel[temp_channel_id]
       -        raise Exception("TODO: create funding transaction using wallet")
       -
       +        remote_pubkey = accept_channel["funding_pubkey"]
       +        if int.from_bytes(remote_pubkey, byteorder="big") < int.from_bytes(funding_pubkey, byteorder="big"):
       +            pubkeys = [remote_pubkey, funding_pubkey]
       +        else:
       +            pubkeys = [funding_pubkey, remote_pubkey]
       +        scr = transaction.multisig_script([binascii.hexlify(x).decode("ascii") for x in pubkeys], 2)
       +        #TODO support passwd, fix fee
       +        tx = wallet.mktx([(bitcoin.TYPE_SCRIPT, scr, 20000)], None, config, 1000)
       +        tx.sign({funding_pubkey: (funding_privkey, True)})
       +        sig = bytes.fromhex(tx.inputs()[0]["signatures"][0])
       +        sig = bytes(sig[:len(sig)-1])
       +        r, s = sigdecode_der(sig, SECP256k1.generator.order())
       +        sig = sigencode_string_canonize(r, s, SECP256k1.generator.order())
       +        print(sig)
       +
       +        self.temporary_channel_id_to_incoming_funding_signed[temp_channel_id] = asyncio.Future()
       +        self.send_message(gen_msg("funding_created", temporary_channel_id=temp_channel_id, funding_txid=bytes.fromhex(tx.txid()), funding_output_index=0, signature=sig))
       +        try:
       +            funding_signed = await self.temporary_channel_id_to_incoming_funding_signed[temp_channel_id]
       +        finally:
       +            del self.temporary_channel_id_to_incoming_funding_signed[temp_channel_id]
        
        # replacement for lightningCall
        class LNWorker:
       t@@ -406,14 +442,14 @@ class LNWorker:
                fut = asyncio.run_coroutine_threadsafe(self._test(q), asyncio.get_event_loop())
                exp = q.get(timeout=5)
                if exp is not None:
       -            raise exp
       +            return exp
                return "blocking test run took: " + str(time.time() - start)
        
            async def _test(self, q):
                try:
       -            await self.peer.channel_establishment_flow(self.wallet)
       -        except Exception as e:
       -            q.put(e)
       +            await self.peer.channel_establishment_flow(self.wallet, self.network.config)
       +        except Exception:
       +            q.put(traceback.format_exc())
                else:
                    q.put(None)