tlnbase: receiving invoice payment works - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 9f8d6625ecc6e957f5b02ffce5db7b139b454158 DIR parent 6d87599964f33dabf314ee98c344f86a2a007835 HTML Author: Janus <ysangkok@gmail.com> Date: Fri, 27 Apr 2018 20:09:32 +0200 lnbase: receiving invoice payment works Diffstat: M lib/lnbase.py | 64 +++++++++++++++++++++++++++---- M lib/tests/test_lnbase_online.py | 4 ---- 2 files changed, 56 insertions(+), 12 deletions(-) --- DIR diff --git a/lib/lnbase.py b/lib/lnbase.py t@@ -879,14 +879,16 @@ class Peer(PrintError): local_next_per_commitment_secret, byteorder="big")) - remote_revocation_pubkey = derive_blinded_pubkey(remote_revocation_basepoint, local_next_per_commitment_point) remote_htlc_pubkey = derive_pubkey(remote_htlc_basepoint, local_next_per_commitment_point) local_htlc_pubkey = derive_pubkey(local_htlc_basepoint, local_next_per_commitment_point) + htlc_id = int.from_bytes(self.unfulfilled_htlcs[0]["id"], "big") + assert htlc_id == 0, htlc_id payment_hash = self.unfulfilled_htlcs[0]["payment_hash"] cltv_expiry = int.from_bytes(self.unfulfilled_htlcs[0]["cltv_expiry"],"big") # TODO verify sanity of their cltv expiry amount_msat = int.from_bytes(self.unfulfilled_htlcs[0]["amount_msat"], "big") + remote_revocation_pubkey = derive_blinded_pubkey(remote_revocation_basepoint, local_next_per_commitment_point) local_ctx_args = local_ctx_args._replace(remote_amount = local_ctx_args.remote_amount - expected_received_sat) local_ctx_args = local_ctx_args._replace(ctn = local_next_commitment_number) local_ctx_args = local_ctx_args._replace(remote_revocation_pubkey = remote_revocation_pubkey) t@@ -910,6 +912,8 @@ class Peer(PrintError): if htlc_sigs_len != 64: raise Exception("unexpected number of htlc signatures: " + str(htlc_sigs_len)) + # TODO verify htlc_signature + pubkeys = sorted([bh2u(local_ctx_args.funding_pubkey), bh2u(remote_funding_pubkey)]) revocation_pubkey = derive_blinded_pubkey(revocation_basepoint, remote_next_commitment_point) remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_next_commitment_point) t@@ -939,19 +943,19 @@ class Peer(PrintError): local_feerate = local_ctx_args.local_feerate, revocationpubkey=revocation_pubkey, local_delayedpubkey=remote_delayedpubkey, - success = False) + success = False) # timeout for the one offering an HTLC preimage_script = htlcs_in_remote[0][0] htlc_output_txid = remote_ctx.txid() htlc_tx_inputs = make_htlc_tx_inputs( htlc_output_txid=htlc_output_txid, - htlc_output_index=1, # TODO find index of htlc output in new_commitment + htlc_output_index=1, # TODO find index of htlc output in remote_ctx revocationpubkey=revocation_pubkey, local_delayedpubkey=remote_delayedpubkey, amount_msat=amount_msat, witness_script=bh2u(preimage_script)) htlc_tx = make_htlc_tx(cltv_expiry, inputs=htlc_tx_inputs, output=htlc_tx_output) - #htlc_sig signs the HTLC transaction that spends from OUR commitment transaction's received_htlc output + # htlc_sig signs the HTLC transaction that spends from THEIR commitment transaction's received_htlc output sig = bfh(htlc_tx.sign_txin(0, their_remote_htlc_privkey)) r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order()) htlc_sig = sigencode_string_canonize(r, s, SECP256k1.generator.order()) t@@ -964,10 +968,54 @@ class Peer(PrintError): finally: del self.revoke_and_ack[channel_id] - #TODO check revoke_and_ack_msg contents + # TODO check revoke_and_ack_msg contents + + self.send_message(gen_msg("update_fulfill_htlc", channel_id=channel_id, id=0, payment_preimage=payment_preimage)) + + remote_next_commitment_point = revoke_and_ack_msg["next_per_commitment_point"] + + revocation_pubkey = derive_blinded_pubkey(revocation_basepoint, remote_next_commitment_point) + remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_next_commitment_point) + local_payment_pubkey = derive_pubkey(local_ctx_args.base_point, remote_next_commitment_point) + # local commitment transaction without htlcs + bare_ctx = make_commitment( + 2, + remote_funding_pubkey, local_ctx_args.funding_pubkey, local_payment_pubkey, + local_ctx_args.base_point, local_ctx_args.remote_payment_basepoint, + revocation_pubkey, remote_delayedpubkey, remote_delay, + local_ctx_args.funding_txid, local_ctx_args.funding_index, local_ctx_args.funding_satoshis, + local_ctx_args.remote_amount, local_ctx_args.local_amount + expected_received_sat, remote_dust_limit_satoshis, local_ctx_args.local_feerate, False, htlcs=[]) + + bare_ctx.sign({bh2u(local_ctx_args.funding_pubkey): (funding_privkey, True)}) + sig_index = pubkeys.index(bh2u(local_ctx_args.funding_pubkey)) + sig = bytes.fromhex(bare_ctx.inputs()[0]["signatures"][sig_index]) + 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.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] + finally: + del self.revoke_and_ack[channel_id] + + # TODO check revoke_and_ack results + + try: + commitment_signed_msg = await self.commitment_signed[channel_id] + finally: + del self.commitment_signed[channel_id] + + 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) + local_next_per_commitment_point = secret_to_pubkey(int.from_bytes( + local_next_per_commitment_secret, + byteorder="big")) + + self.send_message(gen_msg("revoke_and_ack", channel_id=channel_id, per_commitment_secret=local_last_per_commitment_secret, next_per_commitment_point=local_next_per_commitment_point)) - async def fulfill_htlc(self, channel_id, htlc_id, payment_preimage): - self.send_message(gen_msg("update_fulfill_htlc", channel_id=channel_id, id=htlc_id, payment_preimage=payment_preimage)) def on_commitment_signed(self, payload): self.print_error("commitment_signed", payload) t@@ -982,7 +1030,7 @@ class Peer(PrintError): def on_revoke_and_ack(self, payload): channel_id = int.from_bytes(payload["channel_id"], "big") - self.revoke_and_ack[channel_id].set_result(channel_id) + self.revoke_and_ack[channel_id].set_result(payload) DIR diff --git a/lib/tests/test_lnbase_online.py b/lib/tests/test_lnbase_online.py t@@ -56,10 +56,6 @@ if __name__ == "__main__": print("payment request", pay_req) last_pcs_index = 2**48 - 1 await peer.receive_commitment_revoke_ack(channel_id, per_commitment_secret_seed, last_pcs_index, local_ctx_args, expected_received_sat, remote_funding_pubkey, local_next_commitment_number=1, remote_next_commitment_point=remote_funding_locked_msg["next_per_commitment_point"], remote_revocation_basepoint=remote_revocation_basepoint, remote_htlc_basepoint=remote_htlc_basepoint, local_htlc_basepoint=local_htlc_basepoint, delayed_payment_basepoint=delayed_payment_basepoint, revocation_basepoint=revocation_basepoint, remote_delayed_payment_basepoint=remote_delayed_payment_basepoint, remote_delay=remote_delay, remote_dust_limit_satoshis=remote_dust_limit_satoshis, funding_privkey=funding_privkey, htlc_privkey=htlc_privkey, payment_preimage=payment_preimage) - htlc_id = 0 # TODO should correspond with received htlc (when handling more than just one update) - await peer.fulfill_htlc(channel_id, htlc_id, payment_preimage) - while True: - await asyncio.sleep(1) fut = asyncio.run_coroutine_threadsafe(async_test(), network.asyncio_loop) while not fut.done(): time.sleep(1)