URI: 
       tlnbase: fix pay(), save htlc_id's, generate onion packet correctly - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit b81fb449527a5416b7224ed72f2e9a6e536ff9dd
   DIR parent 34da1349e0e7f65844b21335e5d59995a7427502
  HTML Author: Janus <ysangkok@gmail.com>
       Date:   Wed, 16 May 2018 15:42:54 +0200
       
       lnbase: fix pay(), save htlc_id's, generate onion packet correctly
       
       Diffstat:
         M lib/lnbase.py                       |      81 ++++++++++++++++++++++---------
         M lib/tests/test_lnbase_online.py     |      13 ++++++-------
       
       2 files changed, 63 insertions(+), 31 deletions(-)
       ---
   DIR diff --git a/lib/lnbase.py b/lib/lnbase.py
       t@@ -275,8 +275,8 @@ ChannelConfig = namedtuple("ChannelConfig", [
            "payment_basepoint", "multisig_key", "htlc_basepoint", "delayed_basepoint", "revocation_basepoint",
            "to_self_delay", "dust_limit_sat", "max_htlc_value_in_flight_msat", "max_accepted_htlcs"])
        OnlyPubkeyKeypair = namedtuple("OnlyPubkeyKeypair", ["pubkey"])
       -RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_sat", "revocation_store", "last_per_commitment_point"])
       -LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_sat"])
       +RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_sat", "revocation_store", "last_per_commitment_point", "next_htlc_id"])
       +LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_sat", "next_htlc_id"])
        ChannelConstraints = namedtuple("ChannelConstraints", ["feerate", "capacity", "is_initiator", "funding_txn_minimum_depth"])
        OpenChannel = namedtuple("OpenChannel", ["channel_id", "short_channel_id", "funding_outpoint", "local_config", "remote_config", "remote_state", "local_state", "constraints"])
        
       t@@ -563,6 +563,11 @@ def sign_and_get_sig_string(tx, local_config, remote_config):
            sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order())
            return sig_64
        
       +def is_synced(network):
       +    local_height, server_height = network.get_status_value("updated")
       +    synced = server_height != 0 and network.is_up_to_date() and local_height >= server_height
       +    return synced
       +
        class Peer(PrintError):
            def __init__(self, host, port, pubkey, privkey, request_initial_sync=False, network=None):
                self.host = host
       t@@ -606,7 +611,7 @@ class Peer(PrintError):
        
            def send_message(self, msg):
                message_type, payload = decode_msg(msg)
       -        self.print_error("Sending '%s'"%message_type.upper(), payload)
       +        self.print_error("Sending '%s'"%message_type.upper())
                l = len(msg).to_bytes(2, 'big')
                lc = aead_encrypt(self.sk, self.sn(), b'', l)
                c = aead_encrypt(self.sk, self.sn(), b'', msg)
       t@@ -937,12 +942,14 @@ class Peer(PrintError):
                            next_per_commitment_point=None,
                            last_per_commitment_point=remote_per_commitment_point,
                            amount_sat=remote_amount,
       -                    revocation_store=their_revocation_store
       +                    revocation_store=their_revocation_store,
       +                    next_htlc_id = 0
                        ),
                        local_state=LocalState(
                            ctn = 0,
                            per_commitment_secret_seed=per_commitment_secret_seed,
       -                    amount_sat=local_amount
       +                    amount_sat=local_amount,
       +                    next_htlc_id = 0
                        ),
                        constraints=ChannelConstraints(capacity=funding_sat, feerate=local_feerate, is_initiator=True, funding_txn_minimum_depth=funding_txn_minimum_depth)
                )
       t@@ -1040,17 +1047,22 @@ class Peer(PrintError):
                        )
                    )
                    return last_secret, this_point, next_point
       +        their_revstore = chan.remote_state.revocation_store
                sat = int(sat)
       -        cltv_expiry = wallet.get_local_height() + 9
       +        await asyncio.sleep(1)
       +        while not is_synced(wallet.network):
       +            await asyncio.sleep(1)
       +            print("sleeping more")
       +        cltv_expiry = wallet.get_local_height() + chan.remote_config.to_self_delay
                assert sat > 0, "sat is not positive"
                amount_msat = sat * 1000
        
                assert type(self.pubkey) is bytes
                hops_data = [OnionHopsDataSingle(OnionPerHop(chan.short_channel_id, amount_msat.to_bytes(8, "big"), cltv_expiry.to_bytes(4, "big")))]
       -        associated_data = b""
       -        onion = new_onion_packet([self.pubkey], self.privkey, hops_data, associated_data)
       +        associated_data = payment_hash
       +        onion = new_onion_packet([self.pubkey], os.urandom(32), hops_data, associated_data)
        
       -        self.send_message(gen_msg("update_add_htlc", channel_id=chan.channel_id, id=0, cltv_expiry=cltv_expiry, amount_msat=amount_msat, payment_hash=payment_hash, onion_routing_packet=onion.to_bytes()))
       +        self.send_message(gen_msg("update_add_htlc", channel_id=chan.channel_id, id=chan.local_state.next_htlc_id, cltv_expiry=cltv_expiry, amount_msat=amount_msat, payment_hash=payment_hash, onion_routing_packet=onion.to_bytes()))
        
                their_local_htlc_pubkey = derive_pubkey(chan.remote_config.htlc_basepoint.pubkey, chan.remote_state.next_per_commitment_point)
                their_remote_htlc_pubkey = derive_pubkey(chan.local_config.htlc_basepoint.pubkey, chan.remote_state.next_per_commitment_point)
       t@@ -1074,18 +1086,25 @@ class Peer(PrintError):
        
                self.send_message(gen_msg("commitment_signed", channel_id=chan.channel_id, signature=sig_64, num_htlcs=1, htlc_signature=htlc_sig))
        
       +        try:
       +            revoke_and_ack_msg = await self.revoke_and_ack[chan.channel_id]
       +        finally:
       +            del self.revoke_and_ack[chan.channel_id]
       +        # TODO check revoke_and_ack results
       +
                last_secret, _, next_point = derive_and_incr()
       +        their_revstore.add_next_entry(last_secret)
                self.send_message(gen_msg("revoke_and_ack",
                    channel_id=chan.channel_id,
                    per_commitment_secret=last_secret,
                    next_per_commitment_point=next_point))
        
                try:
       -            revoke_and_ack_msg = await self.revoke_and_ack[chan.channel_id]
       +            update_fulfill_htlc_msg = await self.update_fulfill_htlc[chan.channel_id]
                finally:
       -            del self.revoke_and_ack[chan.channel_id]
       +            del self.update_fulfill_htlc[chan.channel_id]
        
       -        # TODO check revoke_and_ack results
       +        # TODO use other fields too
                next_per_commitment_point = revoke_and_ack_msg["next_per_commitment_point"]
        
                try:
       t@@ -1096,26 +1115,37 @@ class Peer(PrintError):
                # TODO check commitment_signed results
        
                last_secret, _, next_point = derive_and_incr()
       +        their_revstore.add_next_entry(last_secret)
                self.send_message(gen_msg("revoke_and_ack",
                    channel_id=chan.channel_id,
                    per_commitment_secret=last_secret,
                    next_per_commitment_point=next_point))
        
       -        try:
       -            update_fulfill_htlc_msg = await self.update_fulfill_htlc[chan.channel_id]
       -        finally:
       -            del self.update_fulfill_htlc[chan.channel_id]
       +        bare_ctx = make_commitment_using_open_channel(chan, chan.remote_state.ctn + 2, False, next_per_commitment_point,
       +            chan.remote_state.amount_sat + sat, chan.local_state.amount_sat - sat)
        
       -        print("update_fulfill_htlc_msg", update_fulfill_htlc_msg)
       +        sig_64 = sign_and_get_sig_string(bare_ctx, chan.local_config, chan.remote_config)
        
       +        self.send_message(gen_msg("commitment_signed", channel_id=chan.channel_id, signature=sig_64, num_htlcs=0))
                try:
       -            commitment_signed_msg = await self.commitment_signed[chan.channel_id]
       +            revoke_and_ack_msg = await self.revoke_and_ack[chan.channel_id]
                finally:
       -            del self.commitment_signed[chan.channel_id]
       -
       -        # TODO send revoke_and_ack
       +            del self.revoke_and_ack[chan.channel_id]
       +        # TODO check revoke_and_ack results
        
       -        # TODO send commitment_signed
       +        return chan._replace(
       +            local_state=chan.local_state._replace(
       +                amount_sat=chan.local_state.amount_sat - sat,
       +                next_htlc_id=chan.local_state.next_htlc_id + 1
       +            ),
       +            remote_state=chan.remote_state._replace(
       +                ctn=chan.remote_state.ctn + 2,
       +                revocation_store=their_revstore,
       +                last_per_commitment_point=next_per_commitment_point,
       +                next_per_commitment_point=revoke_and_ack_msg["next_per_commitment_point"],
       +                amount_sat=chan.remote_state.amount_sat + sat
       +            )
       +        )
        
            async def receive_commitment_revoke_ack(self, chan, expected_received_sat, payment_preimage):
                def derive_and_incr():
       t@@ -1143,8 +1173,10 @@ class Peer(PrintError):
                finally:
                    del self.commitment_signed[channel_id]
        
       +        assert len(self.unfulfilled_htlcs) == 1
                htlc = self.unfulfilled_htlcs.pop()
                htlc_id = int.from_bytes(htlc["id"], 'big')
       +        assert htlc_id == chan.remote_state.next_htlc_id, (htlc_id, chan.remote_state.next_htlc_id)
                cltv_expiry = int.from_bytes(htlc["cltv_expiry"], 'big')
                # TODO verify sanity of their cltv expiry
                amount_msat = int.from_bytes(htlc["amount_msat"], 'big')
       t@@ -1264,7 +1296,8 @@ class Peer(PrintError):
                        revocation_store=their_revstore,
                        last_per_commitment_point=remote_next_commitment_point,
                        next_per_commitment_point=revoke_and_ack_msg["next_per_commitment_point"],
       -                amount_sat=chan.remote_state.amount_sat - expected_received_sat
       +                amount_sat=chan.remote_state.amount_sat - expected_received_sat,
       +                next_htlc_id=htlc_id + 1
                    )
                )
        
       t@@ -1275,7 +1308,7 @@ class Peer(PrintError):
        
            def on_update_fulfill_htlc(self, payload):
                channel_id = int.from_bytes(payload["channel_id"], 'big')
       -        self.update_fulfill_htlc[channel_id].set_reuslt(payload)
       +        self.update_fulfill_htlc[channel_id].set_result(payload)
        
            def on_update_fail_malformed_htlc(self, payload):
                self.on_error(payload)
   DIR diff --git a/lib/tests/test_lnbase_online.py b/lib/tests/test_lnbase_online.py
       t@@ -128,14 +128,13 @@ if __name__ == "__main__":
                    addr = lndecode(sys.argv[6], expected_hrp="sb" if sys.argv[2] == "simnet" else "tb")
                    payment_hash = addr.paymenthash
                    amt = int(addr.amount * COIN)
       -            print("amt", amt)
       -            await peer.pay(wallet, openchannel, amt, payment_hash)
       -            return
       +            advanced_channel = await peer.pay(wallet, openchannel, amt, payment_hash)
       +        else:
       +            expected_received_sat = 200000
       +            pay_req = lnencode(LnAddr(RHASH, amount=1/Decimal(COIN)*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32])
       +            print("payment request", pay_req)
       +            advanced_channel = await peer.receive_commitment_revoke_ack(openchannel, expected_received_sat, payment_preimage)
        
       -        expected_received_sat = 200000
       -        pay_req = lnencode(LnAddr(RHASH, amount=1/Decimal(COIN)*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32])
       -        print("payment request", pay_req)
       -        advanced_channel = await peer.receive_commitment_revoke_ack(openchannel, expected_received_sat, payment_preimage)
                dumped = serialize_channels([advanced_channel])
                wallet.storage.put("channels", dumped)
                wallet.storage.write()