From 468f3b2b8d27f50edacf0c16eda7cedeb48da146 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Tue, 16 Mar 2021 19:07:31 +0100 Subject: [PATCH] lnchannel: verify sig of remote chanupd (for inc edge of direct chan) This is re the channel update for the incoming direction of our own channels. This message can only come from the counterparty itself so maybe the sig check is redundant... but for sanity I think we should check it anyway. --- electrum/channel_db.py | 6 ++++-- electrum/lnchannel.py | 9 ++++++++- electrum/lnpeer.py | 6 +++--- electrum/lnworker.py | 5 +++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/electrum/channel_db.py b/electrum/channel_db.py index 88e4a7d67..49c0934bf 100644 --- a/electrum/channel_db.py +++ b/electrum/channel_db.py @@ -569,12 +569,14 @@ class ChannelDB(SqlDB): c.execute("INSERT INTO address (node_id, host, port, timestamp) VALUES (?,?,?,?)", (addr.pubkey, addr.host, addr.port, 0)) @classmethod - def verify_channel_update(cls, payload) -> None: + def verify_channel_update(cls, payload, *, start_node: bytes = None) -> None: short_channel_id = payload['short_channel_id'] short_channel_id = ShortChannelID(short_channel_id) if constants.net.rev_genesis_bytes() != payload['chain_hash']: raise InvalidGossipMsg('wrong chain hash') - if not verify_sig_for_channel_update(payload, payload['start_node']): + start_node = payload.get('start_node', None) or start_node + assert start_node is not None + if not verify_sig_for_channel_update(payload, start_node): raise InvalidGossipMsg(f'failed verifying channel update for {short_channel_id}') @classmethod diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py index 0b04d3bd8..1d9504df0 100644 --- a/electrum/lnchannel.py +++ b/electrum/lnchannel.py @@ -580,7 +580,14 @@ class Channel(AbstractChannel): raise Exception('lnworker not set for channel!') return self.lnworker.node_keypair.pubkey - def set_remote_update(self, raw: bytes) -> None: + def set_remote_update(self, payload: dict) -> None: + """Save the ChannelUpdate message for the incoming direction of this channel. + This message contains info we need to populate private route hints when + creating invoices. + """ + from .channel_db import ChannelDB + ChannelDB.verify_channel_update(payload, start_node=self.node_id) + raw = payload['raw'] self.storage['remote_update'] = raw.hex() def get_remote_update(self) -> Optional[bytes]: diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index ab072b812..bd4efa802 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -97,7 +97,7 @@ class Peer(Logger): self.funding_signed_sent = set() # for channels in PREOPENING self.shutdown_received = {} # chan_id -> asyncio.Future() self.announcement_signatures = defaultdict(asyncio.Queue) - self.orphan_channel_updates = OrderedDict() + self.orphan_channel_updates = OrderedDict() # type: OrderedDict[ShortChannelID, dict] Logger.__init__(self) self.taskgroup = SilentTaskGroup() # HTLCs offered by REMOTE, that we started removing but are still active: @@ -273,7 +273,7 @@ class Peer(Logger): return for chan in self.channels.values(): if chan.short_channel_id == payload['short_channel_id']: - chan.set_remote_update(payload['raw']) + chan.set_remote_update(payload) self.logger.info("saved remote_update") break else: @@ -1149,7 +1149,7 @@ class Peer(Logger): # peer may have sent us a channel update for the incoming direction previously pending_channel_update = self.orphan_channel_updates.get(chan.short_channel_id) if pending_channel_update: - chan.set_remote_update(pending_channel_update['raw']) + chan.set_remote_update(pending_channel_update) self.logger.info(f"CHANNEL OPENING COMPLETED ({chan.get_id_for_log()})") forwarding_enabled = self.network.config.get('lightning_forward_payments', False) if forwarding_enabled: diff --git a/electrum/lnworker.py b/electrum/lnworker.py index a31b5ebbd..8863fd16d 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -1284,10 +1284,11 @@ class LNWallet(LNWorker): short_channel_id = ShortChannelID(payload['short_channel_id']) if r == UpdateStatus.GOOD: self.logger.info(f"applied channel update to {short_channel_id}") - # TODO: test this + # TODO: add test for this + # FIXME: this does not work for our own unannounced channels. for chan in self.channels.values(): if chan.short_channel_id == short_channel_id: - chan.set_remote_update(payload['raw']) + chan.set_remote_update(payload) update = True elif r == UpdateStatus.ORPHANED: # maybe it is a private channel (and data in invoice was outdated)