electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 2a594e9d0e6628fb5e4b4776e217698daca6d009
parent 913176b4b1d18b0cb652126e04d1de25d52e3cbc
Author: Janus <ysangkok@gmail.com>
Date:   Mon,  7 May 2018 19:01:43 +0200

lnbase: receive repeated payments

Diffstat:
Mlib/lnbase.py | 60+++++++++++++++++++++++++++++++++++-------------------------
Mlib/tests/test_lnbase_online.py | 10++++++----
2 files changed, 41 insertions(+), 29 deletions(-)

diff --git a/lib/lnbase.py b/lib/lnbase.py @@ -913,7 +913,7 @@ class Peer(PrintError): local_config=local_config, remote_config=remote_config, remote_state=RemoteState( - ctn = 0, + ctn = -1, next_per_commitment_point=None, amount_sat=remote_amount, commitment_points=[bh2u(remote_per_commitment_point)] @@ -941,8 +941,8 @@ class Peer(PrintError): # } if channel_reestablish_msg["my_current_per_commitment_point"] != bfh(chan.remote_state.commitment_points[-1]): raise Exception("Remote PCP mismatch") - n = chan.local_state.ctn + 1 - self.send_message(gen_msg("channel_reestablish", channel_id=chan.channel_id, next_local_commitment_number=n, next_remote_revocation_number=chan.remote_state.ctn)) + n = chan.remote_state.ctn + self.send_message(gen_msg("channel_reestablish", channel_id=chan.channel_id, next_local_commitment_number=n+2, next_remote_revocation_number=n+1)) return chan @@ -986,24 +986,24 @@ class Peer(PrintError): async def receive_commitment_revoke_ack(self, chan, expected_received_sat, payment_preimage): channel_id = chan.channel_id local_per_commitment_secret_seed = chan.local_state.per_commitment_secret_seed - assert chan.local_state.ctn == 0 - local_next_pcs_index = 2**48 - 2 try: commitment_signed_msg = await self.commitment_signed[channel_id] finally: del self.commitment_signed[channel_id] - local_next_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_next_pcs_index) + htlc = self.unfulfilled_htlcs.pop() + htlc_id = int.from_bytes(htlc["id"], 'big') + 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') + assert amount_msat // 1000 == expected_received_sat + payment_hash = htlc["payment_hash"] + + local_next_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, 2**48-chan.local_state.ctn-2) local_next_per_commitment_point = secret_to_pubkey(int.from_bytes(local_next_per_commitment_secret, 'big')) remote_htlc_pubkey = derive_pubkey(chan.remote_config.htlc_basepoint.pubkey, local_next_per_commitment_point) local_htlc_pubkey = derive_pubkey(chan.local_config.htlc_basepoint.pubkey, 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(chan.remote_config.revocation_basepoint.pubkey, local_next_per_commitment_point) @@ -1014,7 +1014,7 @@ class Peer(PrintError): ) ] - new_commitment = make_commitment_using_open_channel(chan, 1, True, local_next_per_commitment_point, + new_commitment = make_commitment_using_open_channel(chan, chan.local_state.ctn+1, True, local_next_per_commitment_point, chan.local_state.amount_sat, chan.remote_state.amount_sat - expected_received_sat, htlcs_in_local) @@ -1028,22 +1028,18 @@ class Peer(PrintError): if htlc_sigs_len != 64: raise Exception("unexpected number of htlc signatures: " + str(htlc_sigs_len)) - local_last_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, 2**48-2) - local_last_per_commitment_point = secret_to_pubkey(int.from_bytes( - local_last_per_commitment_secret, - byteorder="big")) + local_last_per_commitment_point = local_next_per_commitment_point htlc_tx = make_htlc_tx_with_open_channel(chan, local_last_per_commitment_point, True, True, amount_msat, cltv_expiry, payment_hash, new_commitment, 0) pre_hash = bitcoin.Hash(bfh(htlc_tx.serialize_preimage(0))) remote_htlc_pubkey = derive_pubkey(chan.remote_config.htlc_basepoint.pubkey, local_last_per_commitment_point) if not bitcoin.verify_signature(remote_htlc_pubkey, commitment_signed_msg["htlc_signature"], pre_hash): raise Exception("failed verifying signature an HTLC tx spending from one of our commit tx'es HTLC outputs") - local_last_pcs_index = 2**48 - chan.local_state.ctn - 1 - local_last_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index) + print("SENDING FIRST REVOKE AND ACK") self.send_message(gen_msg("revoke_and_ack", channel_id=channel_id, - per_commitment_secret=local_last_per_commitment_secret, + per_commitment_secret=get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, 2**48 - (chan.local_state.ctn//2) - 1), next_per_commitment_point=local_next_per_commitment_point)) their_local_htlc_pubkey = derive_pubkey(chan.remote_config.htlc_basepoint.pubkey, chan.remote_state.next_per_commitment_point) @@ -1055,7 +1051,7 @@ class Peer(PrintError): # TODO check payment_hash revocation_pubkey = derive_blinded_pubkey(chan.local_config.revocation_basepoint.pubkey, chan.remote_state.next_per_commitment_point) htlcs_in_remote = [(make_offered_htlc(revocation_pubkey, their_remote_htlc_pubkey, their_local_htlc_pubkey, payment_hash), amount_msat)] - remote_ctx = make_commitment_using_open_channel(chan, 1, False, chan.remote_state.next_per_commitment_point, + remote_ctx = make_commitment_using_open_channel(chan, chan.remote_state.ctn + 2, False, chan.remote_state.next_per_commitment_point, chan.remote_state.amount_sat - expected_received_sat, chan.local_state.amount_sat, htlcs_in_remote) sig_64 = sign_and_get_sig_string(remote_ctx, chan.local_config, chan.remote_config) @@ -1075,12 +1071,12 @@ class Peer(PrintError): # 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)) + self.send_message(gen_msg("update_fulfill_htlc", channel_id=channel_id, id=htlc_id, payment_preimage=payment_preimage)) remote_next_commitment_point = revoke_and_ack_msg["next_per_commitment_point"] # remote commitment transaction without htlcs - bare_ctx = make_commitment_using_open_channel(chan, 2, False, remote_next_commitment_point, + bare_ctx = make_commitment_using_open_channel(chan, chan.remote_state.ctn + 3, False, remote_next_commitment_point, chan.remote_state.amount_sat - expected_received_sat, chan.local_state.amount_sat + expected_received_sat) sig_64 = sign_and_get_sig_string(bare_ctx, chan.local_config, chan.remote_config) @@ -1100,16 +1096,30 @@ class Peer(PrintError): # 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_last_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, 2**48 - chan.local_state.ctn - 2) - 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_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, 2**48 - chan.local_state.ctn - 4) local_next_per_commitment_point = secret_to_pubkey(int.from_bytes(local_next_per_commitment_secret, 'big')) + print("SENDING SECOND REVOKE AND ACK") 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)) + return chan._replace( + local_state=chan.local_state._replace( + ctn=chan.local_state.ctn + 2, + amount_sat=chan.local_state.amount_sat + expected_received_sat + ), + remote_state=chan.remote_state._replace( + ctn=chan.remote_state.ctn + 2, + commitment_points=chan.remote_state.commitment_points + [bh2u(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 + ) + ) + def on_commitment_signed(self, payload): self.print_error("commitment_signed", payload) diff --git a/lib/tests/test_lnbase_online.py b/lib/tests/test_lnbase_online.py @@ -102,7 +102,7 @@ if __name__ == "__main__": # run blocking test async def async_test(): - payment_preimage = bytes.fromhex("01"*32) + payment_preimage = os.urandom(32) RHASH = sha256(payment_preimage) channels = wallet.storage.get("channels", None) @@ -118,11 +118,13 @@ if __name__ == "__main__": openchannel = channels[0] openchannel = reconstruct_namedtuples(openchannel) openchannel = await peer.reestablish_channel(openchannel) - expected_received_sat = 400000 + expected_received_sat = 200000 pay_req = lnencode(LnAddr(RHASH, amount=Decimal("0.00000001")*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32]) print("payment request", pay_req) - last_pcs_index = 2**48 - 1 - await peer.receive_commitment_revoke_ack(openchannel, expected_received_sat, payment_preimage) + 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() fut = asyncio.run_coroutine_threadsafe(async_test(), network.asyncio_loop) while not fut.done(): time.sleep(1)