lnwatcher: replace inspect_tx_candidate with get_spender.
inspect_tx_candidate assumes that htlc transactions have only one input, which is not true for anchor channels. inspect_tx_candidate is still used by the watchtower, because it does not have access to channel information.
This commit is contained in:
@@ -203,18 +203,17 @@ class LNWatcher(Logger, EventListener):
|
||||
# early return if address has not been added yet
|
||||
if not self.adb.is_mine(address):
|
||||
return
|
||||
spenders = self.inspect_tx_candidate(funding_outpoint, 0)
|
||||
# inspect_tx_candidate might have added new addresses, in which case we return early
|
||||
if not self.adb.is_up_to_date():
|
||||
return
|
||||
funding_txid = funding_outpoint.split(':')[0]
|
||||
funding_height = self.adb.get_tx_height(funding_txid)
|
||||
closing_txid = spenders.get(funding_outpoint)
|
||||
closing_txid = self.get_spender(funding_outpoint)
|
||||
closing_height = self.adb.get_tx_height(closing_txid)
|
||||
if closing_txid:
|
||||
closing_tx = self.adb.get_transaction(closing_txid)
|
||||
if closing_tx:
|
||||
keep_watching = await self.sweep_commitment_transaction(funding_outpoint, closing_tx, spenders)
|
||||
keep_watching = await self.sweep_commitment_transaction(funding_outpoint, closing_tx)
|
||||
else:
|
||||
self.logger.info(f"channel {funding_outpoint} closed by {closing_txid}. still waiting for tx itself...")
|
||||
keep_watching = True
|
||||
@@ -230,7 +229,7 @@ class LNWatcher(Logger, EventListener):
|
||||
if not keep_watching:
|
||||
await self.unwatch_channel(address, funding_outpoint)
|
||||
|
||||
async def sweep_commitment_transaction(self, funding_outpoint, closing_tx, spenders) -> bool:
|
||||
async def sweep_commitment_transaction(self, funding_outpoint, closing_tx) -> bool:
|
||||
raise NotImplementedError() # implemented by subclasses
|
||||
|
||||
async def update_channel_state(self, *, funding_outpoint: str, funding_txid: str,
|
||||
@@ -238,6 +237,24 @@ class LNWatcher(Logger, EventListener):
|
||||
closing_height: TxMinedInfo, keep_watching: bool) -> None:
|
||||
raise NotImplementedError() # implemented by subclasses
|
||||
|
||||
|
||||
def get_spender(self, outpoint) -> str:
|
||||
"""
|
||||
returns txid spending outpoint.
|
||||
subscribes to addresses as a side effect.
|
||||
"""
|
||||
prev_txid, index = outpoint.split(':')
|
||||
spender_txid = self.adb.db.get_spent_outpoint(prev_txid, int(index))
|
||||
if not spender_txid:
|
||||
return
|
||||
spender_tx = self.adb.get_transaction(spender_txid)
|
||||
for i, o in enumerate(spender_tx.outputs()):
|
||||
if o.address is None:
|
||||
continue
|
||||
if not self.adb.is_mine(o.address):
|
||||
self.adb.add_address(o.address)
|
||||
return spender_txid
|
||||
|
||||
def inspect_tx_candidate(self, outpoint, n: int) -> Dict[str, str]:
|
||||
"""
|
||||
returns a dict of spenders for a transaction of interest.
|
||||
@@ -261,6 +278,7 @@ class LNWatcher(Logger, EventListener):
|
||||
spender_tx = self.adb.get_transaction(spender_txid)
|
||||
if n == 1:
|
||||
# if tx input is not a first-stage HTLC, we can stop recursion
|
||||
# FIXME: this is not true for anchor channels
|
||||
if len(spender_tx.inputs()) != 1:
|
||||
return result
|
||||
o = spender_tx.inputs()[0]
|
||||
@@ -339,7 +357,8 @@ class WatchTower(LNWatcher):
|
||||
for outpoint, address in random_shuffled_copy(lst):
|
||||
self.add_channel(outpoint, address)
|
||||
|
||||
async def sweep_commitment_transaction(self, funding_outpoint, closing_tx, spenders):
|
||||
async def sweep_commitment_transaction(self, funding_outpoint, closing_tx):
|
||||
spenders = self.inspect_tx_candidate(funding_outpoint, 0)
|
||||
keep_watching = False
|
||||
for prevout, spender in spenders.items():
|
||||
if spender is not None:
|
||||
@@ -436,7 +455,7 @@ class LNWalletWatcher(LNWatcher):
|
||||
await self.lnworker.handle_onchain_state(chan)
|
||||
|
||||
@log_exceptions
|
||||
async def sweep_commitment_transaction(self, funding_outpoint, closing_tx, spenders) -> bool:
|
||||
async def sweep_commitment_transaction(self, funding_outpoint, closing_tx) -> bool:
|
||||
"""This function is called when a channel was closed. In this case
|
||||
we need to check for redeemable outputs of the commitment transaction
|
||||
or spenders down the line (HTLC-timeout/success transactions).
|
||||
@@ -459,18 +478,18 @@ class LNWalletWatcher(LNWatcher):
|
||||
# do not keep watching if prevout does not exist
|
||||
self.logger.info(f'prevout does not exist for {name}: {prev_txid}')
|
||||
continue
|
||||
spender_txid = spenders.get(prevout)
|
||||
spender_txid = self.get_spender(prevout)
|
||||
spender_tx = self.adb.get_transaction(spender_txid) if spender_txid else None
|
||||
if spender_tx:
|
||||
# the spender might be the remote, revoked or not
|
||||
htlc_idx_to_sweepinfo = chan.maybe_sweep_revoked_htlcs(closing_tx, spender_tx)
|
||||
for idx, htlc_revocation_sweep_info in htlc_idx_to_sweepinfo.items():
|
||||
htlc_tx_spender = spenders.get(spender_txid+f':{idx}')
|
||||
htlc_tx_spender = self.get_spender(spender_txid+f':{idx}')
|
||||
if htlc_tx_spender:
|
||||
keep_watching |= not self.is_deeply_mined(htlc_tx_spender)
|
||||
else:
|
||||
keep_watching = True
|
||||
await self.maybe_redeem(spenders, spender_txid+f':{idx}', htlc_revocation_sweep_info, name)
|
||||
await self.maybe_redeem(spender_txid+f':{idx}', htlc_revocation_sweep_info, name)
|
||||
else:
|
||||
keep_watching |= not self.is_deeply_mined(spender_txid)
|
||||
txin_idx = spender_tx.get_input_idx_that_spent_prevout(TxOutpoint.from_str(prevout))
|
||||
@@ -480,14 +499,14 @@ class LNWalletWatcher(LNWatcher):
|
||||
else:
|
||||
keep_watching = True
|
||||
# broadcast or maybe update our own tx
|
||||
await self.maybe_redeem(spenders, prevout, sweep_info, name)
|
||||
await self.maybe_redeem(prevout, sweep_info, name)
|
||||
|
||||
return keep_watching
|
||||
|
||||
def get_redeem_tx(self, spenders, prevout: str, sweep_info: 'SweepInfo', name: str):
|
||||
def get_redeem_tx(self, prevout: str, sweep_info: 'SweepInfo', name: str):
|
||||
# check if redeem tx needs to be updated
|
||||
# if it is in the mempool, we need to check fee rise
|
||||
txid = spenders.get(prevout)
|
||||
txid = self.get_spender(prevout)
|
||||
old_tx = self.adb.get_transaction(txid)
|
||||
assert old_tx is not None or txid is None
|
||||
tx_depth = self.get_tx_mined_depth(txid) if txid else None
|
||||
@@ -517,8 +536,8 @@ class LNWalletWatcher(LNWatcher):
|
||||
assert old_tx is not None
|
||||
return old_tx, None
|
||||
|
||||
async def maybe_redeem(self, spenders, prevout, sweep_info: 'SweepInfo', name: str) -> None:
|
||||
old_tx, new_tx = self.get_redeem_tx(spenders, prevout, sweep_info, name)
|
||||
async def maybe_redeem(self, prevout, sweep_info: 'SweepInfo', name: str) -> None:
|
||||
old_tx, new_tx = self.get_redeem_tx(prevout, sweep_info, name)
|
||||
if new_tx is None:
|
||||
return
|
||||
prev_txid, prev_index = prevout.split(':')
|
||||
|
||||
Reference in New Issue
Block a user