lnchannel: persist error sent by remote peer into db
If a force-close happens due to e.g. a feerate disagreement or an invalid signature, etc, and the remote peer sends us an error, it can be useful if users can provide us with this error. If the user does not have logging enabled when the error is sent, without this persistence it will likely get lost.
This commit is contained in:
@@ -179,7 +179,7 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener):
|
||||
return
|
||||
self.window.show_transaction(tx)
|
||||
|
||||
def get_common_form(self, chan):
|
||||
def get_common_form(self, chan: AbstractChannel):
|
||||
form = QtWidgets.QFormLayout(None)
|
||||
remote_id_e = ShowQRLineEdit(chan.node_id.hex(), self.window.config, title=_("Remote Node ID"))
|
||||
form.addRow(QLabel(_('Remote Node') + ':'), remote_id_e)
|
||||
@@ -191,6 +191,10 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener):
|
||||
if remote_scid_alias := chan.get_remote_scid_alias():
|
||||
form.addRow(QLabel('Remote SCID Alias:'), SelectableLabel(str(ShortID(remote_scid_alias))))
|
||||
form.addRow(QLabel(_('State') + ':'), SelectableLabel(chan.get_state_for_GUI()))
|
||||
if remote_peer_sent_error := chan.get_remote_peer_sent_error():
|
||||
err_label = WWLabel(remote_peer_sent_error) # note: text is already truncated to reasonable len
|
||||
err_label.setTextFormat(QtCore.Qt.TextFormat.PlainText)
|
||||
form.addRow(WWLabel(_('Remote peer sent error [DO NOT TRUST]') + ':'), err_label)
|
||||
self.capacity = self.format_sat(chan.get_capacity())
|
||||
form.addRow(QLabel(_('Capacity') + ':'), SelectableLabel(self.capacity))
|
||||
if not chan.is_backup():
|
||||
|
||||
@@ -37,7 +37,7 @@ import electrum_ecc as ecc
|
||||
from electrum_ecc import ECPubkey
|
||||
|
||||
from . import constants, util
|
||||
from .util import bfh, chunks, TxMinedInfo
|
||||
from .util import bfh, chunks, TxMinedInfo, error_text_bytes_to_safe_str
|
||||
from .invoices import PR_PAID
|
||||
from .bitcoin import redeem_script_to_address
|
||||
from .crypto import sha256, sha256d
|
||||
@@ -303,6 +303,9 @@ class AbstractChannel(Logger, ABC):
|
||||
def get_remote_scid_alias(self) -> Optional[bytes]:
|
||||
return None
|
||||
|
||||
def get_remote_peer_sent_error(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
def get_ctx_sweep_info(self, ctx: Transaction) -> Tuple[bool, Dict[str, SweepInfo]]:
|
||||
our_sweep_info = self.create_sweeptxs_for_our_ctx(ctx)
|
||||
their_sweep_info = self.create_sweeptxs_for_their_ctx(ctx)
|
||||
@@ -892,6 +895,21 @@ class Channel(AbstractChannel):
|
||||
net_addr = NetAddress.from_string(net_addr_str)
|
||||
yield LNPeerAddr(host=str(net_addr.host), port=net_addr.port, pubkey=self.node_id)
|
||||
|
||||
def save_remote_peer_sent_error(self, original_error: bytes):
|
||||
# We save the original arbitrary text(/bytes) error, as received.
|
||||
# The length is only implicitly limited by the BOLT-08 max msg size.
|
||||
# Receiving an error usually results in the channel getting closed, so
|
||||
# there is likely no need to store multiple errors. We only store one, and overwrite.
|
||||
self.storage['remote_peer_sent_error'] = original_error.hex()
|
||||
|
||||
def get_remote_peer_sent_error(self) -> Optional[str]:
|
||||
original_error = self.storage.get('remote_peer_sent_error')
|
||||
if not original_error:
|
||||
return None
|
||||
err_bytes = bytes.fromhex(original_error)
|
||||
safe_str = error_text_bytes_to_safe_str(err_bytes) # note: truncates
|
||||
return safe_str
|
||||
|
||||
def get_outgoing_gossip_channel_update(self, *, scid: ShortChannelID = None) -> bytes:
|
||||
"""
|
||||
scid: to be put into the channel_update message instead of the real scid, as this might be an scid alias
|
||||
|
||||
@@ -282,9 +282,10 @@ class Peer(Logger, EventListener):
|
||||
self.logger.info(f"remote peer sent error [DO NOT TRUST THIS MESSAGE]: "
|
||||
f"{error_text_bytes_to_safe_str(err_bytes, max_len=None)}. chan_id={chan_id.hex()}. "
|
||||
f"{is_known_chan_id=}")
|
||||
if chan_id in self.channels:
|
||||
if chan := self.channels.get(chan_id):
|
||||
self.schedule_force_closing(chan_id)
|
||||
self.ordered_message_queues[chan_id].put_nowait((None, {'error': err_bytes}))
|
||||
chan.save_remote_peer_sent_error(err_bytes)
|
||||
elif chan_id in self.temp_id_to_id:
|
||||
chan_id = self.temp_id_to_id[chan_id] or chan_id
|
||||
self.ordered_message_queues[chan_id].put_nowait((None, {'error': err_bytes}))
|
||||
|
||||
Reference in New Issue
Block a user