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."""