lnpeer: if close_channel times out, check unconfirmed_closing_txid before raising an exception
This commit is contained in:
@@ -304,7 +304,7 @@ class AbstractChannel(Logger, ABC):
|
|||||||
# we must not trust the server with unconfirmed transactions,
|
# we must not trust the server with unconfirmed transactions,
|
||||||
# because the state transition is irreversible. if the remote
|
# because the state transition is irreversible. if the remote
|
||||||
# force closed, we remain OPEN until the closing tx is confirmed
|
# force closed, we remain OPEN until the closing tx is confirmed
|
||||||
self.closing_detected = True
|
self.unconfirmed_closing_txid = closing_txid
|
||||||
if self.lnworker:
|
if self.lnworker:
|
||||||
util.trigger_callback('channel', self.lnworker.wallet, self)
|
util.trigger_callback('channel', self.lnworker.wallet, self)
|
||||||
|
|
||||||
@@ -339,7 +339,7 @@ class AbstractChannel(Logger, ABC):
|
|||||||
|
|
||||||
def get_state_for_GUI(self) -> str:
|
def get_state_for_GUI(self) -> str:
|
||||||
cs = self.get_state()
|
cs = self.get_state()
|
||||||
if cs == ChannelState.OPEN and self.closing_detected:
|
if cs == ChannelState.OPEN and self.unconfirmed_closing_txid:
|
||||||
return 'FORCE-CLOSING'
|
return 'FORCE-CLOSING'
|
||||||
return cs.name
|
return cs.name
|
||||||
|
|
||||||
@@ -419,7 +419,7 @@ class ChannelBackup(AbstractChannel):
|
|||||||
self.config = {}
|
self.config = {}
|
||||||
if self.is_imported:
|
if self.is_imported:
|
||||||
self.init_config(cb)
|
self.init_config(cb)
|
||||||
self.closing_detected = False # not a state, only for GUI
|
self.unconfirmed_closing_txid = None # not a state, only for GUI
|
||||||
|
|
||||||
def init_config(self, cb):
|
def init_config(self, cb):
|
||||||
self.config[LOCAL] = LocalConfig.from_seed(
|
self.config[LOCAL] = LocalConfig.from_seed(
|
||||||
@@ -553,7 +553,7 @@ class Channel(AbstractChannel):
|
|||||||
self._receive_fail_reasons = {} # type: Dict[int, (bytes, OnionRoutingFailure)]
|
self._receive_fail_reasons = {} # type: Dict[int, (bytes, OnionRoutingFailure)]
|
||||||
self._ignore_max_htlc_value = False # used in tests
|
self._ignore_max_htlc_value = False # used in tests
|
||||||
self.should_request_force_close = False
|
self.should_request_force_close = False
|
||||||
self.closing_detected = False # not a state, only for GUI
|
self.unconfirmed_closing_txid = None # not a state, only for GUI
|
||||||
|
|
||||||
def has_onchain_backup(self):
|
def has_onchain_backup(self):
|
||||||
return self.storage.get('has_onchain_backup', False)
|
return self.storage.get('has_onchain_backup', False)
|
||||||
@@ -735,7 +735,7 @@ class Channel(AbstractChannel):
|
|||||||
|
|
||||||
def get_state_for_GUI(self):
|
def get_state_for_GUI(self):
|
||||||
cs_name = super().get_state_for_GUI()
|
cs_name = super().get_state_for_GUI()
|
||||||
if self.is_closed() or self.closing_detected:
|
if self.is_closed() or self.unconfirmed_closing_txid:
|
||||||
return cs_name
|
return cs_name
|
||||||
ps = self.peer_state
|
ps = self.peer_state
|
||||||
if ps != PeerState.GOOD:
|
if ps != PeerState.GOOD:
|
||||||
|
|||||||
@@ -1692,19 +1692,23 @@ class Peer(Logger):
|
|||||||
self.shutdown_received[chan_id] = asyncio.Future()
|
self.shutdown_received[chan_id] = asyncio.Future()
|
||||||
await self.send_shutdown(chan)
|
await self.send_shutdown(chan)
|
||||||
payload = await self.shutdown_received[chan_id]
|
payload = await self.shutdown_received[chan_id]
|
||||||
txid = await self._shutdown(chan, payload, is_local=True)
|
try:
|
||||||
self.logger.info(f'({chan.get_id_for_log()}) Channel closed {txid}')
|
txid = await self._shutdown(chan, payload, is_local=True)
|
||||||
|
self.logger.info(f'({chan.get_id_for_log()}) Channel closed {txid}')
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
txid = chan.unconfirmed_closing_txid
|
||||||
|
self.logger.info(f'({chan.get_id_for_log()}) did not send closing_signed, {txid}')
|
||||||
|
if txid is None:
|
||||||
|
raise Exception('The remote peer did not send their final signature. The channel may not have been be closed')
|
||||||
return txid
|
return txid
|
||||||
|
|
||||||
async def on_shutdown(self, chan: Channel, payload):
|
async def on_shutdown(self, chan: Channel, payload):
|
||||||
their_scriptpubkey = payload['scriptpubkey']
|
their_scriptpubkey = payload['scriptpubkey']
|
||||||
their_upfront_scriptpubkey = chan.config[REMOTE].upfront_shutdown_script
|
their_upfront_scriptpubkey = chan.config[REMOTE].upfront_shutdown_script
|
||||||
|
|
||||||
# BOLT-02 check if they use the upfront shutdown script they advertized
|
# BOLT-02 check if they use the upfront shutdown script they advertized
|
||||||
if their_upfront_scriptpubkey:
|
if their_upfront_scriptpubkey:
|
||||||
if not (their_scriptpubkey == their_upfront_scriptpubkey):
|
if not (their_scriptpubkey == their_upfront_scriptpubkey):
|
||||||
raise UpfrontShutdownScriptViolation("remote didn't use upfront shutdown script it commited to in channel opening")
|
raise UpfrontShutdownScriptViolation("remote didn't use upfront shutdown script it commited to in channel opening")
|
||||||
|
|
||||||
# BOLT-02 restrict the scriptpubkey to some templates:
|
# BOLT-02 restrict the scriptpubkey to some templates:
|
||||||
if not (match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_WITNESS_V0)
|
if not (match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_WITNESS_V0)
|
||||||
or match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_P2SH)
|
or match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_P2SH)
|
||||||
@@ -1731,13 +1735,11 @@ class Peer(Logger):
|
|||||||
async def send_shutdown(self, chan: Channel):
|
async def send_shutdown(self, chan: Channel):
|
||||||
if not self.can_send_shutdown(chan):
|
if not self.can_send_shutdown(chan):
|
||||||
raise Exception('cannot send shutdown')
|
raise Exception('cannot send shutdown')
|
||||||
|
|
||||||
if chan.config[LOCAL].upfront_shutdown_script:
|
if chan.config[LOCAL].upfront_shutdown_script:
|
||||||
scriptpubkey = chan.config[LOCAL].upfront_shutdown_script
|
scriptpubkey = chan.config[LOCAL].upfront_shutdown_script
|
||||||
else:
|
else:
|
||||||
scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address))
|
scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address))
|
||||||
assert scriptpubkey
|
assert scriptpubkey
|
||||||
|
|
||||||
# wait until no more pending updates (bolt2)
|
# wait until no more pending updates (bolt2)
|
||||||
chan.set_can_send_ctx_updates(False)
|
chan.set_can_send_ctx_updates(False)
|
||||||
while chan.has_pending_changes(REMOTE):
|
while chan.has_pending_changes(REMOTE):
|
||||||
@@ -1761,7 +1763,6 @@ class Peer(Logger):
|
|||||||
else:
|
else:
|
||||||
our_scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address))
|
our_scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address))
|
||||||
assert our_scriptpubkey
|
assert our_scriptpubkey
|
||||||
|
|
||||||
# estimate fee of closing tx
|
# estimate fee of closing tx
|
||||||
our_sig, closing_tx = chan.make_closing_tx(our_scriptpubkey, their_scriptpubkey, fee_sat=0)
|
our_sig, closing_tx = chan.make_closing_tx(our_scriptpubkey, their_scriptpubkey, fee_sat=0)
|
||||||
fee_rate = self.network.config.fee_per_kb()
|
fee_rate = self.network.config.fee_per_kb()
|
||||||
|
|||||||
Reference in New Issue
Block a user