URI: 
       tlnbase: allow passing KeypairGenerator to channel_establishment_flow, fix derive_privkey - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit c2bbc1ec6050963db654b18013ce39616729a0f9
   DIR parent 9f8d6625ecc6e957f5b02ffce5db7b139b454158
  HTML Author: Janus <ysangkok@gmail.com>
       Date:   Mon, 30 Apr 2018 17:07:52 +0200
       
       lnbase: allow passing KeypairGenerator to channel_establishment_flow, fix derive_privkey
       
       Diffstat:
         M lib/lnbase.py                       |      80 +++++++++++++++++++-------------
       
       1 file changed, 48 insertions(+), 32 deletions(-)
       ---
   DIR diff --git a/lib/lnbase.py b/lib/lnbase.py
       t@@ -31,7 +31,7 @@ from . import transaction
        from .util import PrintError, bh2u, print_error, bfh, profiler
        from .transaction import opcodes, Transaction
        
       -from collections import namedtuple
       +from collections import namedtuple, defaultdict
        LocalCtxArgs = namedtuple("LocalCtxArgs",
                    ["ctn",
                    "funding_pubkey", "remote_funding_pubkey", "remotepubkey",
       t@@ -289,14 +289,15 @@ def create_ephemeral_key(privkey):
            pub = privkey_to_pubkey(privkey)
            return (privkey[:32], pub)
        
       -def get_unused_keys():
       -    xprv, xpub = bitcoin.bip32_root(b"testseed", "p2wpkh")
       -    for i in itertools.count():
       -        childxprv, childxpub = bitcoin.bip32_private_derivation(xprv, "m/", "m/42/"+str(i))
       +class KeypairGenerator:
       +    def __init__(self, seed):
       +        self.xprv, xpub = bitcoin.bip32_root(seed, "p2wpkh")
       +    def get(self, keyfamily, index):
       +        childxprv, childxpub = bitcoin.bip32_private_derivation(self.xprv, "m/", "m/42'/{keyfamily}'/{index}'".format(keyfamily=keyfamily, index=index))
                _, _, _, _, child_c, child_cK = bitcoin.deserialize_xpub(childxpub)
                _, _, _, _, _, k = bitcoin.deserialize_xprv(childxprv)
                assert len(k) == 32
       -        yield child_cK, k
       +        return child_cK, k
        
        def aiosafe(f):
            async def f2(*args, **kwargs):
       t@@ -326,7 +327,9 @@ def derive_pubkey(basepoint, per_commitment_point):
        def derive_privkey(secret, per_commitment_point):
            assert type(secret) is int
            basepoint = point_to_ser(SECP256k1.generator * secret)
       -    return secret + bitcoin.string_to_number(bitcoin.sha256(per_commitment_point + basepoint))
       +    basepoint = secret + bitcoin.string_to_number(bitcoin.sha256(per_commitment_point + basepoint))
       +    basepoint %= SECP256k1.order
       +    return basepoint
        
        def derive_blinded_pubkey(basepoint, per_commitment_point):
            k1 = ser_to_point(basepoint) * bitcoin.string_to_number(bitcoin.sha256(basepoint + per_commitment_point))
       t@@ -503,12 +506,12 @@ class Peer(PrintError):
                    "remote_funding_locked",
                    "revoke_and_ack",
                    "commitment_signed"]
       -        self.channel_accepted = {}
       -        self.funding_signed = {}
       -        self.local_funding_locked = {}
       -        self.remote_funding_locked = {}
       -        self.revoke_and_ack = {}
       -        self.commitment_signed = {}
       +        self.channel_accepted = defaultdict(asyncio.Future)
       +        self.funding_signed = defaultdict(asyncio.Future)
       +        self.local_funding_locked = defaultdict(asyncio.Future)
       +        self.remote_funding_locked = defaultdict(asyncio.Future)
       +        self.revoke_and_ack = defaultdict(asyncio.Future)
       +        self.commitment_signed = defaultdict(asyncio.Future)
                self.initialized = asyncio.Future()
                self.localfeatures = (0x08 if request_initial_sync else 0)
                # view of the network
       t@@ -707,16 +710,27 @@ class Peer(PrintError):
                self.writer.close()
        
            @aiosafe
       -    async def channel_establishment_flow(self, wallet, config, funding_satoshis, push_msat):
       +    async def channel_establishment_flow(self, wallet, config, funding_satoshis, push_msat, temp_channel_id, keypair_generator=None):
                await self.initialized
       -        temp_channel_id = os.urandom(32)
       -        keys = get_unused_keys()
       -        funding_pubkey, funding_privkey = next(keys)
       -        revocation_basepoint, revocation_privkey = next(keys)
       -        htlc_basepoint, htlc_privkey = next(keys)
       -        delayed_payment_basepoint, delayed_privkey = next(keys)
       -        base_secret = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
       -        per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(length=32, byteorder="big")
       +
       +        if keypair_generator is None:
       +            keypair_generator = KeypairGenerator(b"testseed")
       +
       +        # see lnd/keychain/derivation.go
       +        keyfamilymultisig = 0
       +        keyfamilyrevocationbase = 1
       +        keyfamilyhtlcbase = 2
       +        keyfamilypaymentbase = 3
       +        keyfamilydelaybase = 4
       +        keyfamilyrevocationroot = 5
       +        keyfamilynodekey = 6 # TODO currently unused
       +
       +        funding_pubkey, funding_privkey = keypair_generator.get(keyfamilymultisig, 0)
       +        revocation_basepoint, _ = keypair_generator.get(keyfamilyrevocationbase, 0)
       +        htlc_basepoint, htlc_privkey = keypair_generator.get(keyfamilyhtlcbase, 0)
       +        delayed_payment_basepoint, delayed_privkey = keypair_generator.get(keyfamilydelaybase, 0)
       +        base_point, _ = keypair_generator.get(keyfamilypaymentbase, 0)
       +        per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(32, "big")
                per_commitment_secret_index = 2**48 - 1
                # amounts
                local_feerate = 20000
       t@@ -724,7 +738,6 @@ class Peer(PrintError):
                to_self_delay = 144
                ctn = 0
                #
       -        base_point = secret_to_pubkey(base_secret)
                per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, per_commitment_secret_index)
                per_commitment_point_first = secret_to_pubkey(int.from_bytes(
                    per_commitment_secret_first,
       t@@ -747,7 +760,7 @@ class Peer(PrintError):
                    to_self_delay=to_self_delay,
                    max_htlc_value_in_flight_msat=500000 * 1000
                )
       -        self.channel_accepted[temp_channel_id] = asyncio.Future()
       +        #self.channel_accepted[temp_channel_id] = asyncio.Future()
                self.send_message(msg)
                try:
                    payload = await self.channel_accepted[temp_channel_id]
       t@@ -808,7 +821,7 @@ class Peer(PrintError):
                funding_txid_bytes = bytes.fromhex(funding_txid)[::-1]
                channel_id = int.from_bytes(funding_txid_bytes, byteorder="big") ^ funding_index
                self.send_message(gen_msg("funding_created", temporary_channel_id=temp_channel_id, funding_txid=funding_txid_bytes, funding_output_index=funding_index, signature=sig_64))
       -        self.funding_signed[channel_id] = asyncio.Future()
       +        #self.funding_signed[channel_id] = asyncio.Future()
                try:
                    payload = await self.funding_signed[channel_id]
                finally:
       t@@ -828,8 +841,8 @@ class Peer(PrintError):
                if not bitcoin.verify_signature(remote_funding_pubkey, remote_sig, pre_hash):
                    raise Exception('verifying remote signature failed.')
                # broadcast funding tx
       -        self.local_funding_locked[channel_id] = asyncio.Future()
       -        self.remote_funding_locked[channel_id] = asyncio.Future()
       +        #self.local_funding_locked[channel_id] = asyncio.Future()
       +        #self.remote_funding_locked[channel_id] = asyncio.Future()
                success, _txid = self.network.broadcast(funding_tx)
                assert success, success
                # wait until we see confirmations
       t@@ -862,7 +875,7 @@ class Peer(PrintError):
                finally:
                    del self.remote_funding_locked[channel_id]
                self.print_error('Done waiting for remote_funding_locked', remote_funding_locked_msg)
       -        self.commitment_signed[channel_id] = asyncio.Future()
       +        #self.commitment_signed[channel_id] = asyncio.Future()
                return channel_id, per_commitment_secret_seed, local_ctx_args, remote_funding_pubkey, remote_funding_locked_msg, remote_revocation_basepoint, remote_htlc_basepoint, htlc_basepoint, delayed_payment_basepoint, revocation_basepoint, remote_delayed_payment_basepoint, remote_delay, remote_dust_limit_satoshis, funding_privkey, htlc_privkey
        
            async def receive_commitment_revoke_ack(self, channel_id, local_per_commitment_secret_seed, local_last_pcs_index, local_ctx_args, expected_received_sat, remote_funding_pubkey, local_next_commitment_number, remote_next_commitment_point, remote_revocation_basepoint, remote_htlc_basepoint, local_htlc_basepoint, delayed_payment_basepoint, revocation_basepoint, remote_delayed_payment_basepoint, remote_delay, remote_dust_limit_satoshis, funding_privkey, payment_preimage, htlc_privkey):
       t@@ -919,7 +932,8 @@ class Peer(PrintError):
                remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_next_commitment_point)
                their_local_htlc_pubkey = derive_pubkey(remote_htlc_basepoint, remote_next_commitment_point)
                their_remote_htlc_pubkey = derive_pubkey(local_htlc_basepoint, remote_next_commitment_point)
       -        their_remote_htlc_privkey = derive_privkey(int.from_bytes(htlc_privkey, "big"), remote_next_commitment_point).to_bytes(32, "big")
       +        their_remote_htlc_privkey_number = derive_privkey(int.from_bytes(htlc_privkey, "big"), remote_next_commitment_point)
       +        their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, "big")
                # TODO check payment_hash
                htlcs_in_remote = [(make_offered_htlc(revocation_pubkey, their_remote_htlc_pubkey, their_local_htlc_pubkey, payment_hash), amount_msat)]
                local_payment_pubkey = derive_pubkey(local_ctx_args.base_point, remote_next_commitment_point)
       t@@ -960,7 +974,7 @@ class Peer(PrintError):
                r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order())
                htlc_sig = sigencode_string_canonize(r, s, SECP256k1.generator.order())
        
       -        self.revoke_and_ack[channel_id] = asyncio.Future()
       +        #self.revoke_and_ack[channel_id] = asyncio.Future()
                self.send_message(gen_msg("commitment_signed", channel_id=channel_id, signature=sig_64, num_htlcs=1, htlc_signature=htlc_sig))
        
                try:
       t@@ -992,8 +1006,8 @@ class Peer(PrintError):
                r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order())
                sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order())
        
       -        self.revoke_and_ack[channel_id] = asyncio.Future()
       -        self.commitment_signed[channel_id] = asyncio.Future() # we will also receive a new commitment transaction shortly after sending ours
       +        #self.revoke_and_ack[channel_id] = asyncio.Future()
       +        #self.commitment_signed[channel_id] = asyncio.Future() # we will also receive a new commitment transaction shortly after sending ours
                self.send_message(gen_msg("commitment_signed", channel_id=channel_id, signature=sig_64, num_htlcs=0))
                try:
                    revoke_and_ack_msg = await self.revoke_and_ack[channel_id]
       t@@ -1007,6 +1021,8 @@ class Peer(PrintError):
                finally:
                    del self.commitment_signed[channel_id]
        
       +        # TODO check commitment_signed results
       +
                local_last_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index - 1)
        
                local_next_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index - 2)