Browse Source

fix #6122: extract preimage from on-chain htlc_tx

master
ThomasV 5 years ago
parent
commit
8ba7e68064
  1. 15
      electrum/lnchannel.py
  2. 3
      electrum/lnwatcher.py
  3. 3
      electrum/tests/regtest.py
  4. 33
      electrum/tests/regtest/regtest.sh
  5. 6
      electrum/transaction.py

15
electrum/lnchannel.py

@ -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

3
electrum/lnwatcher.py

@ -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()
@ -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)

3
electrum/tests/regtest.py

@ -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'])

33
electrum/tests/regtest/regtest.sh

@ -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

6
electrum/transaction.py

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

Loading…
Cancel
Save