electrum

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

commit 740ef0988362648ad78f1845a2ddab9c7909e2de
parent 9abbd077a528d7d602a4d2260a00614a7235432a
Author: ThomasV <thomasv@electrum.org>
Date:   Mon,  3 Jun 2019 22:00:44 +0200

simplify_lnsweep

Diffstat:
Melectrum/lnsweep.py | 166+++++++++++++++++++++++++++++++------------------------------------------------
Melectrum/lnworker.py | 5+----
2 files changed, 65 insertions(+), 106 deletions(-)

diff --git a/electrum/lnsweep.py b/electrum/lnsweep.py @@ -100,94 +100,47 @@ class ChannelClosedBy(Enum): def detect_who_closed(chan: 'Channel', ctx: Transaction) -> ChannelClosedBy: ctn = extract_ctn_from_tx_and_chan(ctx, chan) - our_conf, their_conf = get_ordered_channel_configs(chan=chan, for_us=True) + sweep_info = create_sweeptxs_for_our_ctx(chan, ctx, ctn, chan.sweep_address) + if sweep_info: + return ChannelClosedBy.US, sweep_info + sweep_info = create_sweeptxs_for_their_ctx(chan, ctx, ctn, chan.sweep_address) + if sweep_info: + return ChannelClosedBy.THEM, sweep_info + return ChannelClosedBy.UNKNOWN, {} - def get_to_local_and_to_remote_addresses_for_our_ctx(): - is_breach = ctn < our_conf.ctn - # to_local - our_per_commitment_secret = get_per_commitment_secret_from_seed( - our_conf.per_commitment_secret_seed, RevocationStore.START_INDEX - ctn) - our_pcp = ecc.ECPrivkey(our_per_commitment_secret).get_public_key_bytes(compressed=True) - our_delayed_bp_privkey = ecc.ECPrivkey(our_conf.delayed_basepoint.privkey) - our_localdelayed_privkey = derive_privkey(our_delayed_bp_privkey.secret_scalar, our_pcp) - our_localdelayed_privkey = ecc.ECPrivkey.from_secret_scalar(our_localdelayed_privkey) - their_revocation_pubkey = derive_blinded_pubkey(their_conf.revocation_basepoint.pubkey, our_pcp) - our_localdelayed_pubkey = our_localdelayed_privkey.get_public_key_bytes(compressed=True) - to_local_witness_script = bh2u(make_commitment_output_to_local_witness_script( - their_revocation_pubkey, their_conf.to_self_delay, our_localdelayed_pubkey)) - to_local_address = redeem_script_to_address('p2wsh', to_local_witness_script) - # to_remote - their_payment_pubkey = derive_pubkey(their_conf.payment_basepoint.pubkey, our_pcp) - to_remote_address = make_commitment_output_to_remote_address(their_payment_pubkey) - return to_local_address, to_remote_address, is_breach - - def get_to_local_and_to_remote_addresses_for_their_ctx(): - is_breach = False - if ctn == their_conf.ctn: - their_pcp = their_conf.current_per_commitment_point - elif ctn == their_conf.ctn + 1: - their_pcp = their_conf.next_per_commitment_point - elif ctn < their_conf.ctn: # breach - is_breach = True - try: - per_commitment_secret = their_conf.revocation_store.retrieve_secret(RevocationStore.START_INDEX - ctn) - except UnableToDeriveSecret: - return None, None, is_breach - their_pcp = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True) - else: - return None, None, None - # to_local - our_revocation_pubkey = derive_blinded_pubkey(our_conf.revocation_basepoint.pubkey, their_pcp) - their_delayed_pubkey = derive_pubkey(their_conf.delayed_basepoint.pubkey, their_pcp) - witness_script = bh2u(make_commitment_output_to_local_witness_script( - our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey)) - to_local_address = redeem_script_to_address('p2wsh', witness_script) - # to_remote - our_payment_pubkey = derive_pubkey(our_conf.payment_basepoint.pubkey, their_pcp) - to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey) - return to_local_address, to_remote_address, is_breach - - # our ctx? - to_local_address, to_remote_address, is_breach = get_to_local_and_to_remote_addresses_for_our_ctx() - if (to_local_address and ctx.get_output_idx_from_address(to_local_address) is not None - or to_remote_address and ctx.get_output_idx_from_address(to_remote_address) is not None): - return ChannelClosedBy.US - - # their ctx? - to_local_address, to_remote_address, is_breach = get_to_local_and_to_remote_addresses_for_their_ctx() - if (to_local_address and ctx.get_output_idx_from_address(to_local_address) is not None - or to_remote_address and ctx.get_output_idx_from_address(to_remote_address) is not None): - return ChannelClosedBy.THEM - - return ChannelClosedBy.UNKNOWN - - -def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, + +def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int, sweep_address: str) -> Dict[str,Transaction]: """Handle the case where we force close unilaterally with our latest ctx. Construct sweep txns for 'to_local', and for all HTLCs (2 txns each). 'to_local' can be swept even if this is a breach (by us), but HTLCs cannot (old HTLCs are no longer stored). """ - this_conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=True) - ctn = extract_ctn_from_tx_and_chan(ctx, chan) + our_conf, their_conf = get_ordered_channel_configs(chan=chan, for_us=True) our_per_commitment_secret = get_per_commitment_secret_from_seed( - this_conf.per_commitment_secret_seed, RevocationStore.START_INDEX - ctn) + our_conf.per_commitment_secret_seed, RevocationStore.START_INDEX - ctn) our_pcp = ecc.ECPrivkey(our_per_commitment_secret).get_public_key_bytes(compressed=True) - # prep - this_delayed_bp_privkey = ecc.ECPrivkey(this_conf.delayed_basepoint.privkey) - our_localdelayed_privkey = derive_privkey(this_delayed_bp_privkey.secret_scalar, our_pcp) + our_delayed_bp_privkey = ecc.ECPrivkey(our_conf.delayed_basepoint.privkey) + our_localdelayed_privkey = derive_privkey(our_delayed_bp_privkey.secret_scalar, our_pcp) our_localdelayed_privkey = ecc.ECPrivkey.from_secret_scalar(our_localdelayed_privkey) - remote_revocation_pubkey = derive_blinded_pubkey(other_conf.revocation_basepoint.pubkey, our_pcp) - to_self_delay = chan.config[REMOTE].to_self_delay - this_htlc_privkey = derive_privkey(secret=int.from_bytes(this_conf.htlc_basepoint.privkey, 'big'), + their_revocation_pubkey = derive_blinded_pubkey(their_conf.revocation_basepoint.pubkey, our_pcp) + to_self_delay = their_conf.to_self_delay + our_htlc_privkey = derive_privkey(secret=int.from_bytes(our_conf.htlc_basepoint.privkey, 'big'), per_commitment_point=our_pcp).to_bytes(32, 'big') - txs = {} - # to_local our_localdelayed_pubkey = our_localdelayed_privkey.get_public_key_bytes(compressed=True) to_local_witness_script = bh2u(make_commitment_output_to_local_witness_script( - remote_revocation_pubkey, to_self_delay, our_localdelayed_pubkey)) + their_revocation_pubkey, to_self_delay, our_localdelayed_pubkey)) to_local_address = redeem_script_to_address('p2wsh', to_local_witness_script) + their_payment_pubkey = derive_pubkey(their_conf.payment_basepoint.pubkey, our_pcp) + to_remote_address = make_commitment_output_to_remote_address(their_payment_pubkey) + + # test ctx + if ctx.get_output_idx_from_address(to_local_address) is None\ + and ctx.get_output_idx_from_address(to_remote_address) is None: + return + + txs = {} + # to_local output_idx = ctx.get_output_idx_from_address(to_local_address) if output_idx is not None: sweep_tx = lambda: create_sweeptx_ctx_to_local( @@ -215,7 +168,7 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, our_pcp=our_pcp, ctx=ctx, htlc=htlc, - local_htlc_privkey=this_htlc_privkey, + local_htlc_privkey=our_htlc_privkey, preimage=preimage, is_received_htlc=is_received_htlc) sweep_tx = lambda: create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx( @@ -240,47 +193,56 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, return txs -def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, +def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, ctn: int, sweep_address: str) -> Dict[str,Transaction]: """Handle the case when the remote force-closes with their ctx. Sweep outputs that do not have a CSV delay ('to_remote' and first-stage HTLCs). Outputs with CSV delay ('to_local' and second-stage HTLCs) are redeemed by LNWatcher. """ - this_conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=False) + our_conf, their_conf = get_ordered_channel_configs(chan=chan, for_us=True) ctn = extract_ctn_from_tx_and_chan(ctx, chan) # note: the remote sometimes has two valid non-revoked commitment transactions, - # either of which could be broadcast (this_conf.ctn, this_conf.ctn+1) + # either of which could be broadcast (their_conf.ctn, their_conf.ctn+1) per_commitment_secret = None - if ctn == this_conf.ctn: - their_pcp = this_conf.current_per_commitment_point + if ctn == their_conf.ctn: + their_pcp = their_conf.current_per_commitment_point is_revocation = False - elif ctn == this_conf.ctn + 1: - their_pcp = this_conf.next_per_commitment_point + elif ctn == their_conf.ctn + 1: + their_pcp = their_conf.next_per_commitment_point is_revocation = False - elif ctn < this_conf.ctn: # breach + elif ctn < their_conf.ctn: # breach try: - per_commitment_secret = this_conf.revocation_store.retrieve_secret(RevocationStore.START_INDEX - ctn) + per_commitment_secret = their_conf.revocation_store.retrieve_secret(RevocationStore.START_INDEX - ctn) except UnableToDeriveSecret: - return {} + return their_pcp = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True) is_revocation = True - other_revocation_privkey = derive_blinded_privkey(other_conf.revocation_basepoint.privkey, per_commitment_secret) + our_revocation_privkey = derive_blinded_privkey(our_conf.revocation_basepoint.privkey, per_commitment_secret) else: - return {} + return + # to_local and to_remote addresses + our_revocation_pubkey = derive_blinded_pubkey(our_conf.revocation_basepoint.pubkey, their_pcp) + their_delayed_pubkey = derive_pubkey(their_conf.delayed_basepoint.pubkey, their_pcp) + witness_script = bh2u(make_commitment_output_to_local_witness_script( + our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey)) + to_local_address = redeem_script_to_address('p2wsh', witness_script) + our_payment_pubkey = derive_pubkey(our_conf.payment_basepoint.pubkey, their_pcp) + to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey) + # test ctx + if ctx.get_output_idx_from_address(to_local_address) is None \ + and ctx.get_output_idx_from_address(to_remote_address) is None: + return # prep - other_revocation_pubkey = derive_blinded_pubkey(other_conf.revocation_basepoint.pubkey, their_pcp) - other_htlc_privkey = derive_privkey(secret=int.from_bytes(other_conf.htlc_basepoint.privkey, 'big'), per_commitment_point=their_pcp) - other_htlc_privkey = ecc.ECPrivkey.from_secret_scalar(other_htlc_privkey) - this_htlc_pubkey = derive_pubkey(this_conf.htlc_basepoint.pubkey, their_pcp) - other_payment_bp_privkey = ecc.ECPrivkey(other_conf.payment_basepoint.privkey) - other_payment_privkey = derive_privkey(other_payment_bp_privkey.secret_scalar, their_pcp) - other_payment_privkey = ecc.ECPrivkey.from_secret_scalar(other_payment_privkey) - + our_htlc_privkey = derive_privkey(secret=int.from_bytes(our_conf.htlc_basepoint.privkey, 'big'), per_commitment_point=their_pcp) + our_htlc_privkey = ecc.ECPrivkey.from_secret_scalar(our_htlc_privkey) + their_htlc_pubkey = derive_pubkey(their_conf.htlc_basepoint.pubkey, their_pcp) + our_payment_bp_privkey = ecc.ECPrivkey(our_conf.payment_basepoint.privkey) + our_payment_privkey = derive_privkey(our_payment_bp_privkey.secret_scalar, their_pcp) + our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey) + assert our_payment_pubkey == our_payment_privkey.get_public_key_bytes(compressed=True) txs = {} # to_local is handled by lnwatcher # to_remote - our_payment_pubkey = other_payment_privkey.get_public_key_bytes(compressed=True) - to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey) output_idx = ctx.get_output_idx_from_address(to_remote_address) if output_idx is not None: prevout = ctx.txid() + ':%d'%output_idx @@ -288,7 +250,7 @@ def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, sweep_address=sweep_address, ctx=ctx, output_idx=output_idx, - our_payment_privkey=other_payment_privkey) + our_payment_privkey=our_payment_privkey) txs[prevout] = ('their_ctx_to_remote', 0, 0, sweep_tx) # HTLCs def create_sweeptx_for_htlc(htlc: 'UpdateAddHtlc', is_received_htlc: bool) -> Optional[Transaction]: @@ -302,9 +264,9 @@ def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, preimage = None htlc_output_witness_script = make_htlc_output_witness_script( is_received_htlc=is_received_htlc, - remote_revocation_pubkey=other_revocation_pubkey, - remote_htlc_pubkey=other_htlc_privkey.get_public_key_bytes(compressed=True), - local_htlc_pubkey=this_htlc_pubkey, + remote_revocation_pubkey=our_revocation_pubkey, + remote_htlc_pubkey=our_htlc_privkey.get_public_key_bytes(compressed=True), + local_htlc_pubkey=their_htlc_pubkey, payment_hash=htlc.payment_hash, cltv_expiry=htlc.cltv_expiry) htlc_address = redeem_script_to_address('p2wsh', bh2u(htlc_output_witness_script)) @@ -320,7 +282,7 @@ def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, sweep_address=sweep_address, preimage=preimage, output_idx=output_idx, - privkey=other_revocation_privkey if is_revocation else other_htlc_privkey.get_secret_bytes(), + privkey=our_revocation_privkey if is_revocation else our_htlc_privkey.get_secret_bytes(), is_revocation=is_revocation, cltv_expiry=cltv_expiry) name = f'their_ctx_sweep_htlc_{ctx.txid()[:8]}_{output_idx}' diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -516,16 +516,13 @@ class LNWallet(LNWorker): # detect who closed and set sweep_info if chan.sweep_info is None: - closed_by = lnsweep.detect_who_closed(chan, closing_tx) + closed_by, chan.sweep_info = lnsweep.detect_who_closed(chan, closing_tx) if closed_by == ChannelClosedBy.US: self.logger.info(f'we force closed {funding_outpoint}.') - chan.sweep_info = create_sweeptxs_for_our_ctx(chan, closing_tx, chan.sweep_address) elif closed_by == ChannelClosedBy.THEM: self.logger.info(f'they force closed {funding_outpoint}.') - chan.sweep_info = create_sweeptxs_for_their_ctx(chan, closing_tx, chan.sweep_address) else: self.logger.info(f'not sure who closed {funding_outpoint} {closing_txid}.') - chan.sweep_info = {} self.logger.info(f'{repr(chan.sweep_info)}') # create and broadcast transaction