Browse Source

small clean-up re "extract preimage from on-chain htlc_tx"

related: #6122
master
SomberNight 5 years ago
parent
commit
62be1cc367
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 68
      electrum/lnchannel.py
  2. 7
      electrum/lnwatcher.py
  3. 14
      electrum/transaction.py

68
electrum/lnchannel.py

@ -28,6 +28,7 @@ from typing import (Optional, Dict, List, Tuple, NamedTuple, Set, Callable,
import time import time
import threading import threading
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import itertools
from aiorpcx import NetAddress from aiorpcx import NetAddress
import attr import attr
@ -37,7 +38,7 @@ from . import constants, util
from .util import bfh, bh2u, chunks, TxMinedInfo, PR_PAID from .util import bfh, bh2u, chunks, TxMinedInfo, PR_PAID
from .bitcoin import redeem_script_to_address from .bitcoin import redeem_script_to_address
from .crypto import sha256, sha256d from .crypto import sha256, sha256d
from .transaction import Transaction, PartialTransaction from .transaction import Transaction, PartialTransaction, TxInput
from .logging import Logger from .logging import Logger
from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailureMessage from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailureMessage
from . import lnutil from . import lnutil
@ -971,46 +972,37 @@ class Channel(AbstractChannel):
failure_message = OnionRoutingFailureMessage.from_bytes(bytes.fromhex(failure_hex)) if failure_hex else None failure_message = OnionRoutingFailureMessage.from_bytes(bytes.fromhex(failure_hex)) if failure_hex else None
return error_bytes, failure_message return error_bytes, failure_message
def extract_preimage_from_htlc_tx(self, tx): def extract_preimage_from_htlc_txin(self, txin: TxInput) -> None:
for _input in tx.inputs(): witness = txin.witness_elements()
witness = _input.witness_elements() if len(witness) == 5: # HTLC success tx
if len(witness) == 5: preimage = witness[3]
preimage = witness[3] elif len(witness) == 3: # spending offered HTLC directly from ctx
elif len(witness) == 3: preimage = witness[1]
preimage = witness[1] else:
else: return
continue payment_hash = sha256(preimage)
payment_hash = sha256(preimage) for direction, htlc in itertools.chain(self.hm.get_htlcs_in_oldest_unrevoked_ctx(REMOTE),
for direction, htlc in self.hm.get_htlcs_in_oldest_unrevoked_ctx(REMOTE): self.hm.get_htlcs_in_latest_ctx(REMOTE)):
if htlc.payment_hash == payment_hash:
is_sent = direction == RECEIVED
break
else:
for direction, htlc in itertools.chain(self.hm.get_htlcs_in_oldest_unrevoked_ctx(LOCAL),
self.hm.get_htlcs_in_latest_ctx(LOCAL)):
if htlc.payment_hash == payment_hash: if htlc.payment_hash == payment_hash:
is_sent = direction == RECEIVED is_sent = direction == SENT
break break
else: else:
for direction, htlc in self.hm.get_htlcs_in_latest_ctx(REMOTE): return
if htlc.payment_hash == payment_hash: if self.lnworker.get_preimage(payment_hash) is None:
is_sent = direction == RECEIVED self.logger.info(f'found preimage for {payment_hash.hex()} in witness of length {len(witness)}')
break self.lnworker.save_preimage(payment_hash, preimage)
else: info = self.lnworker.get_payment_info(payment_hash)
for direction, htlc in self.hm.get_htlcs_in_oldest_unrevoked_ctx(LOCAL): if info is not None and info.status != PR_PAID:
if htlc.payment_hash == payment_hash: if is_sent:
is_sent = direction == SENT self.lnworker.payment_sent(self, payment_hash)
break else:
else: self.lnworker.payment_received(self, payment_hash)
for direction, htlc in self.hm.get_htlcs_in_latest_ctx(LOCAL):
if htlc.payment_hash == payment_hash:
is_sent = direction == SENT
break
else:
continue
if self.lnworker.get_preimage(payment_hash) is None:
self.logger.info(f'found preimage for {payment_hash.hex()} in witness of length {len(witness)}')
self.lnworker.save_preimage(payment_hash, preimage)
info = self.lnworker.get_payment_info(payment_hash)
if info is not None and info.status != PR_PAID:
if is_sent:
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: def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
assert type(whose) is HTLCOwner assert type(whose) is HTLCOwner

7
electrum/lnwatcher.py

@ -13,7 +13,7 @@ from .sql_db import SqlDB, sql
from .wallet_db import WalletDB from .wallet_db import WalletDB
from .util import bh2u, bfh, log_exceptions, ignore_exceptions, TxMinedInfo from .util import bh2u, bfh, log_exceptions, ignore_exceptions, TxMinedInfo
from .address_synchronizer import AddressSynchronizer, TX_HEIGHT_LOCAL, TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED from .address_synchronizer import AddressSynchronizer, TX_HEIGHT_LOCAL, TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED
from .transaction import Transaction from .transaction import Transaction, TxOutpoint
if TYPE_CHECKING: if TYPE_CHECKING:
from .network import Network from .network import Network
@ -387,7 +387,10 @@ class LNWalletWatcher(LNWatcher):
else: else:
self.logger.info(f'(chan {chan.get_id_for_log()}) outpoint already spent {name}: {prevout}') self.logger.info(f'(chan {chan.get_id_for_log()}) outpoint already spent {name}: {prevout}')
keep_watching |= not self.is_deeply_mined(spender_txid) keep_watching |= not self.is_deeply_mined(spender_txid)
chan.extract_preimage_from_htlc_tx(spender_tx) txin_idx = spender_tx.get_input_idx_that_spent_prevout(TxOutpoint.from_str(prevout))
assert txin_idx is not None
spender_txin = spender_tx.inputs()[txin_idx]
chan.extract_preimage_from_htlc_txin(spender_txin)
else: else:
self.logger.info(f'(chan {chan.get_id_for_log()}) trying to redeem {name}: {prevout}') self.logger.info(f'(chan {chan.get_id_for_log()}) trying to redeem {name}: {prevout}')
await self.try_redeem(prevout, sweep_info, name) await self.try_redeem(prevout, sweep_info, name)

14
electrum/transaction.py

@ -953,6 +953,20 @@ class Transaction:
else: else:
raise Exception('output not found', addr) raise Exception('output not found', addr)
def get_input_idx_that_spent_prevout(self, prevout: TxOutpoint) -> Optional[int]:
# build cache if there isn't one yet
# note: can become stale and return incorrect data
# if the tx is modified later; that's out of scope.
if not hasattr(self, '_prevout_to_input_idx'):
d = {} # type: Dict[TxOutpoint, int]
for i, txin in enumerate(self.inputs()):
d[txin.prevout] = i
self._prevout_to_input_idx = d
idx = self._prevout_to_input_idx.get(prevout)
if idx is not None:
assert self.inputs()[idx].prevout == prevout
return idx
def convert_raw_tx_to_hex(raw: Union[str, bytes]) -> str: def convert_raw_tx_to_hex(raw: Union[str, bytes]) -> str:
"""Sanitizes tx-describing input (hex/base43/base64) into """Sanitizes tx-describing input (hex/base43/base64) into

Loading…
Cancel
Save