trework on_channel_closed in LNWorker: - use detect_who_closed; this allows us to redeem to_remote of breach ctx - do not redeem to_local of breach ctx, because it is redundant with lnwatcher - rename a few methods - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 6bbdbf75961dbda5bac4566c3447e4d7d5beccaa DIR parent 930d21c31c5240fe57ed5f53335637e4f3ac5335 HTML Author: ThomasV <thomasv@electrum.org> Date: Sat, 25 May 2019 06:02:23 +0200 rework on_channel_closed in LNWorker: - use detect_who_closed; this allows us to redeem to_remote of breach ctx - do not redeem to_local of breach ctx, because it is redundant with lnwatcher - rename a few methods Diffstat: M electrum/lnchannel.py | 10 +++------- M electrum/lnsweep.py | 37 +++++++++---------------------- M electrum/lnworker.py | 37 ++++++++++++++++--------------- 3 files changed, 33 insertions(+), 51 deletions(-) --- DIR diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py t@@ -46,8 +46,7 @@ from .lnutil import (Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKey HTLC_TIMEOUT_WEIGHT, HTLC_SUCCESS_WEIGHT, extract_ctn_from_tx_and_chan, UpdateAddHtlc, funding_output_script, SENT, RECEIVED, LOCAL, REMOTE, HTLCOwner, make_commitment_outputs, ScriptHtlc, PaymentFailure, calc_onchain_fees, RemoteMisbehaving, make_htlc_output_witness_script) -from .lnsweep import create_sweeptxs_for_their_just_revoked_ctx -from .lnsweep import create_sweeptxs_for_our_latest_ctx, create_sweeptxs_for_their_latest_ctx +from .lnsweep import create_sweeptxs_for_their_revoked_ctx from .lnhtlc import HTLCManager t@@ -168,6 +167,7 @@ class Channel(Logger): self.local_commitment = None self.remote_commitment = None + self.sweep_info = None def get_payments(self): out = {} t@@ -186,13 +186,9 @@ class Channel(Logger): ctn = extract_ctn_from_tx_and_chan(ctx, self) assert self.signature_fits(ctx), (self.hm.log[LOCAL]) self.local_commitment = ctx - if self.sweep_address is not None: - self.local_sweeptxs = create_sweeptxs_for_our_latest_ctx(self, self.local_commitment, self.sweep_address) def set_remote_commitment(self): self.remote_commitment = self.current_commitment(REMOTE) - if self.sweep_address is not None: - self.remote_sweeptxs = create_sweeptxs_for_their_latest_ctx(self, self.remote_commitment, self.sweep_address) def open_with_first_pcp(self, remote_pcp, remote_sig): self.remote_commitment_to_be_revoked = self.pending_commitment(REMOTE) t@@ -460,7 +456,7 @@ class Channel(Logger): return outpoint = self.funding_outpoint.to_str() ctx = self.remote_commitment_to_be_revoked # FIXME can't we just reconstruct it? - sweeptxs = create_sweeptxs_for_their_just_revoked_ctx(self, ctx, per_commitment_secret, self.sweep_address) + sweeptxs = create_sweeptxs_for_their_revoked_ctx(self, ctx, per_commitment_secret, self.sweep_address) for prev_txid, tx in sweeptxs.items(): if tx is not None: self.lnwatcher.add_sweep_tx(outpoint, prev_txid, str(tx)) DIR diff --git a/electrum/lnsweep.py b/electrum/lnsweep.py t@@ -56,8 +56,8 @@ def maybe_create_sweeptx_for_their_ctx_to_local(ctx: Transaction, revocation_pri return sweep_tx -def create_sweeptxs_for_their_just_revoked_ctx(chan: 'Channel', ctx: Transaction, per_commitment_secret: bytes, - sweep_address: str) -> Dict[str,Transaction]: +def create_sweeptxs_for_their_revoked_ctx(chan: 'Channel', ctx: Transaction, per_commitment_secret: bytes, + sweep_address: str) -> Dict[str,Transaction]: """Presign sweeping transactions using the just received revoked pcs. These will only be utilised if the remote breaches. Sweep 'to_local', and all the HTLCs (two cases: directly from ctx, or from HTLC tx). t@@ -135,12 +135,8 @@ class ChannelClosedBy(Enum): UNKNOWN = auto() -class ChannelCloseSituationReport(NamedTuple): - closed_by: ChannelClosedBy - is_breach: Optional[bool] - -def detect_how_channel_was_closed(chan: 'Channel', ctx: Transaction) -> ChannelCloseSituationReport: +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) t@@ -193,19 +189,19 @@ def detect_how_channel_was_closed(chan: 'Channel', ctx: Transaction) -> ChannelC 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 ChannelCloseSituationReport(closed_by=ChannelClosedBy.US, is_breach=is_breach) + 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 ChannelCloseSituationReport(closed_by=ChannelClosedBy.THEM, is_breach=is_breach) + return ChannelClosedBy.THEM - return ChannelCloseSituationReport(closed_by=ChannelClosedBy.UNKNOWN, is_breach=None) + return ChannelClosedBy.UNKNOWN -def create_sweeptxs_for_our_latest_ctx(chan: 'Channel', ctx: Transaction, - sweep_address: str) -> Dict[str,Transaction]: +def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, + 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), t@@ -277,8 +273,8 @@ def create_sweeptxs_for_our_latest_ctx(chan: 'Channel', ctx: Transaction, return txs -def create_sweeptxs_for_their_latest_ctx(chan: 'Channel', ctx: Transaction, - sweep_address: str) -> Dict[str,Transaction]: +def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, + sweep_address: str) -> Dict[str,Transaction]: """Handle the case when the remote force-closes with their ctx. Regardless of it is a breach or not, construct sweep tx for 'to_remote'. If it is a breach, also construct sweep tx for 'to_local'. t@@ -313,18 +309,7 @@ def create_sweeptxs_for_their_latest_ctx(chan: 'Channel', ctx: Transaction, other_payment_privkey = ecc.ECPrivkey.from_secret_scalar(other_payment_privkey) txs = {} - if per_commitment_secret: # breach - # to_local - other_revocation_privkey = derive_blinded_privkey(other_conf.revocation_basepoint.privkey, - per_commitment_secret) - this_delayed_pubkey = derive_pubkey(this_conf.delayed_basepoint.pubkey, their_pcp) - sweep_tx = maybe_create_sweeptx_for_their_ctx_to_local(ctx=ctx, - revocation_privkey=other_revocation_privkey, - to_self_delay=other_conf.to_self_delay, - delayed_pubkey=this_delayed_pubkey, - sweep_address=sweep_address) - if sweep_tx: - txs[sweep_tx.prevout(0)] = sweep_tx + # to_local is handled by lnwatcher # to_remote sweep_tx = maybe_create_sweeptx_for_their_ctx_to_remote(ctx=ctx, sweep_address=sweep_address, DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py t@@ -46,6 +46,7 @@ from .lnrouter import RouteEdge, is_route_sane_to_use from .address_synchronizer import TX_HEIGHT_LOCAL from . import lnsweep from .lnsweep import ChannelClosedBy +from .lnsweep import create_sweeptxs_for_their_ctx, create_sweeptxs_for_our_ctx if TYPE_CHECKING: from .network import Network t@@ -511,24 +512,24 @@ class LNWallet(LNWorker): # remove from channel_db if chan.short_channel_id is not None: self.channel_db.remove_channel(chan.short_channel_id) - # detect who closed - assert closing_tx, f"no closing tx... {repr(closing_tx)}" - sitrep = lnsweep.detect_how_channel_was_closed(chan, closing_tx) - if sitrep.closed_by == ChannelClosedBy.US: - self.logger.info(f'we force closed {funding_outpoint}. sitrep: {repr(sitrep)}') - encumbered_sweeptxs = chan.local_sweeptxs - elif sitrep.closed_by == ChannelClosedBy.THEM and sitrep.is_breach is False: - self.logger.info(f'they force closed {funding_outpoint}. sitrep: {repr(sitrep)}') - encumbered_sweeptxs = chan.remote_sweeptxs - else: - self.logger.info(f'not sure who closed {funding_outpoint} {closing_txid}. sitrep: {repr(sitrep)}') - return - # sweep - for prevout, spender in spenders.items(): - e_tx = encumbered_sweeptxs.get(prevout) - if e_tx is None: - continue - if spender is not None: + + # detect who closed and set sweep_info + if chan.sweep_info is None: + closed_by = 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 + for prevout, e_tx in chan.sweep_info.items(): + if spenders.get(prevout) is not None: self.logger.info(f'outpoint already spent {prevout}') continue prev_txid, prev_index = prevout.split(':')