URI: 
       tfix #6122: extract preimage from on-chain htlc_tx - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 8ba7e6806428a90ffe01293e2b0b8bd340d07c6b
   DIR parent f8019d9b6ca9d1e1e656f057aad1435975616ad5
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sat,  2 May 2020 11:39:21 +0200
       
       fix #6122: extract preimage from on-chain htlc_tx
       
       Diffstat:
         M electrum/lnchannel.py               |      15 +++++++++++++++
         M electrum/lnwatcher.py               |       3 ++-
         M electrum/tests/regtest.py           |       3 +++
         M electrum/tests/regtest/regtest.sh   |      33 +++++++++++++++++++++++++++++++
         M electrum/transaction.py             |       6 ++++++
       
       5 files changed, 59 insertions(+), 1 deletion(-)
       ---
   DIR diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py
       t@@ -956,6 +956,21 @@ class Channel(AbstractChannel):
                        payment_attempt = self._receive_fail_reasons.get(htlc.htlc_id)
                        self.lnworker.payment_failed(self, htlc.payment_hash, payment_attempt)
        
       +    def extract_preimage_from_htlc_tx(self, tx):
       +        witness = tx.inputs()[0].witness_elements()
       +        if len(witness) != 5:
       +            return
       +        preimage = witness[3]
       +        payment_hash = sha256(preimage)
       +        for direction, htlc in self.hm.get_htlcs_in_oldest_unrevoked_ctx(REMOTE):
       +            if htlc.payment_hash == payment_hash:
       +                self.logger.info(f'found preimage for {payment_hash.hex()} in tx witness')
       +                self.lnworker.save_preimage(payment_hash, preimage)
       +                if direction == RECEIVED:
       +                    self.lnworker.payment_sent(self, payment_hash)
       +                else:
       +                    self.lnworker.payment_received(self, payment_hash)
       +
            def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
                assert type(whose) is HTLCOwner
                initial = self.config[whose].initial_msat
   DIR diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py
       t@@ -364,7 +364,7 @@ class LNWalletWatcher(LNWatcher):
                # detect who closed and set sweep_info
                sweep_info_dict = chan.sweep_ctx(closing_tx)
                keep_watching = False if sweep_info_dict else not self.is_deeply_mined(closing_tx.txid())
       -        self.logger.info(f'(chan {chan.get_id_for_log()}) sweep_info_dict length: {len(sweep_info_dict)}')
       +        self.logger.info(f'(chan {chan.get_id_for_log()}) sweep_info_dict {[x.name for x in sweep_info_dict.values()]}')
                # create and broadcast transaction
                for prevout, sweep_info in sweep_info_dict.items():
                    name = sweep_info.name + ' ' + chan.get_id_for_log()
       t@@ -387,6 +387,7 @@ class LNWalletWatcher(LNWatcher):
                        else:
                            self.logger.info(f'(chan {chan.get_id_for_log()}) outpoint already spent {name}: {prevout}')
                            keep_watching |= not self.is_deeply_mined(spender_txid)
       +                    chan.extract_preimage_from_htlc_tx(spender_tx)
                    else:
                        self.logger.info(f'(chan {chan.get_id_for_log()}) trying to redeem {name}: {prevout}')
                        await self.try_redeem(prevout, sweep_info, name)
   DIR diff --git a/electrum/tests/regtest.py b/electrum/tests/regtest.py
       t@@ -39,6 +39,9 @@ class TestLightningAB(TestLightning):
            def test_breach(self):
                self.run_shell(['breach'])
        
       +    def test_extract_preimage(self):
       +        self.run_shell(['extract_preimage'])
       +
            def test_redeem_htlcs(self):
                self.run_shell(['redeem_htlcs'])
        
   DIR diff --git a/electrum/tests/regtest/regtest.sh b/electrum/tests/regtest/regtest.sh
       t@@ -162,6 +162,39 @@ if [[ $1 == "breach" ]]; then
            $bob getbalance
        fi
        
       +
       +if [[ $1 == "extract_preimage" ]]; then
       +    # instead of settling bob will broadcast
       +    $bob enable_htlc_settle false
       +    wait_for_balance alice 1
       +    echo "alice opens channel"
       +    bob_node=$($bob nodeid)
       +    $alice open_channel $bob_node 0.15
       +    new_blocks 3
       +    wait_until_channel_open alice
       +    chan_id=$($alice list_channels | jq -r ".[0].channel_point")
       +    # alice pays bob
       +    invoice=$($bob add_lightning_request 0.04 -m "test")
       +    screen -S alice_payment -dm -L -Logfile /tmp/alice/screen.log $alice lnpay $invoice --timeout=600
       +    sleep 1
       +    unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
       +    if [[ "$unsettled" == "0" ]]; then
       +        echo 'enable_htlc_settle did not work'
       +        exit 1
       +    fi
       +    # bob force closes
       +    $bob close_channel $chan_id --force
       +    new_blocks 1
       +    wait_until_channel_closed bob
       +    sleep 5
       +    success=$(cat /tmp/alice/screen.log | jq -r ".success")
       +    if [[ "$success" != "true" ]]; then
       +        exit 1
       +    fi
       +    cat /tmp/alice/screen.log
       +fi
       +
       +
        if [[ $1 == "redeem_htlcs" ]]; then
            $bob enable_htlc_settle false
            wait_for_balance alice 1
   DIR diff --git a/electrum/transaction.py b/electrum/transaction.py
       t@@ -235,6 +235,12 @@ class TxInput:
                    d['witness'] = self.witness.hex()
                return d
        
       +    def witness_elements(self)-> Sequence[bytes]:
       +        vds = BCDataStream()
       +        vds.write(self.witness)
       +        n = vds.read_compact_size()
       +        return list(vds.read_bytes(vds.read_compact_size()) for i in range(n))
       +
        
        class BCDataStream(object):
            """Workalike python implementation of Bitcoin's CDataStream class."""