diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index ea8779a00..5e83affdb 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -55,6 +55,7 @@ from .interface import GracefulDisconnect from .json_db import StoredDict from .invoices import PR_PAID from .fee_policy import FEE_LN_ETA_TARGET, FEERATE_PER_KW_MIN_RELAY_LIGHTNING +from .channel_db import FLAG_DIRECTION if TYPE_CHECKING: from .lnworker import LNGossip, LNWallet @@ -470,6 +471,12 @@ class Peer(Logger, EventListener): return for chan in self.channels.values(): if payload['short_channel_id'] in [chan.short_channel_id, chan.get_local_scid_alias()]: + # originator: node_id_1 if the least-significant bit of flags is 0 or node_id_2 otherwise + flags = int.from_bytes(payload['channel_flags'], byteorder='big', signed=False) + originator = sorted(self.node_ids)[flags & FLAG_DIRECTION] + if originator == self.lnworker.node_keypair.pubkey: + self.logger.debug(f"peer sent us our own channel update for chan {chan.get_id_for_log()}") + return chan.set_remote_update(payload) self.logger.info(f"saved remote channel_update gossip msg for chan {chan.get_id_for_log()}") break diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py index 06ce9fe54..6ba3f750a 100644 --- a/tests/test_lnpeer.py +++ b/tests/test_lnpeer.py @@ -38,7 +38,7 @@ from electrum.crypto import privkey_to_pubkey from electrum.lnutil import Keypair, PaymentFailure, LnFeatures, HTLCOwner, PaymentFeeBudget, RECEIVED from electrum.lnchannel import ChannelState, PeerState, Channel from electrum.lnrouter import LNPathFinder, PathEdge, LNPathInconsistent -from electrum.channel_db import ChannelDB +from electrum.channel_db import ChannelDB, InvalidGossipMsg from electrum.lnworker import LNWallet, NoPathFound, SentHtlcInfo, PaySession, LNPeerManager from electrum.lnmsg import encode_msg, decode_msg from electrum import lnmsg @@ -607,6 +607,32 @@ class TestPeerUtils(TestPeer): Peer.decode_short_ids(encoded_unsupported) self.assertIn("unexpected first byte", str(ctx.exception)) + async def test_maybe_save_remote_update(self): + graph = self.prepare_chans_and_peers_in_graph(self.GRAPH_DEFINITIONS['single_chan']) + alice_bob_peer, bob_alice_peer = graph.peers[('alice', 'bob')], graph.peers[('bob', 'alice')] + alice_bob_chan, bob_alice_chan = graph.channels[('alice', 'bob')], graph.channels[('bob', 'alice')] + + # prepare channel update from alice + alice_to_bob_chan_update = alice_bob_chan.get_outgoing_gossip_channel_update() + raw = alice_to_bob_chan_update + payload = decode_msg(alice_to_bob_chan_update)[1] + payload['raw'] = raw + + # bob should accept the update and save it + self.assertIsNone(bob_alice_chan.storage.get('remote_update')) + bob_alice_peer.maybe_save_remote_update(payload) + self.assertEqual(bob_alice_chan.storage.get('remote_update'), raw.hex()) + + # alice shouldn't save her own channel update as remote update + self.assertIsNone(alice_bob_chan.storage.get('remote_update')) + alice_bob_peer.maybe_save_remote_update(payload) + self.assertIsNone(alice_bob_chan.storage.get('remote_update')) + + ChannelDB.verify_channel_update(payload, start_node=bob_alice_peer.pubkey) + # trying to verify the sig against the wrong pubkey should fail obviously + with self.assertRaises(InvalidGossipMsg): + ChannelDB.verify_channel_update(payload, start_node=alice_bob_peer.pubkey) + class TestPeerDirect(TestPeer):