diff --git a/electrum/lnutil.py b/electrum/lnutil.py index 78794124d..9a5012f8d 100644 --- a/electrum/lnutil.py +++ b/electrum/lnutil.py @@ -1090,12 +1090,19 @@ class LnFeatures(IntFlag): _ln_feature_contexts[OPTION_SUPPORT_LARGE_CHANNEL_OPT] = (LNFC.INIT | LNFC.NODE_ANN) _ln_feature_contexts[OPTION_SUPPORT_LARGE_CHANNEL_REQ] = (LNFC.INIT | LNFC.NODE_ANN) - # This is still a temporary number. Also used by Eclair. - OPTION_TRAMPOLINE_ROUTING_REQ = 1 << 148 - OPTION_TRAMPOLINE_ROUTING_OPT = 1 << 149 + # Temporary number. + OPTION_TRAMPOLINE_ROUTING_REQ_ECLAIR = 1 << 148 + OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR = 1 << 149 - _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_REQ] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE) - _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_OPT] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE) + _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_REQ_ECLAIR] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE) + _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE) + + # We use a different bit because Phoenix cannot do end-to-end multi-trampoline routes + OPTION_TRAMPOLINE_ROUTING_REQ_ELECTRUM = 1 << 150 + OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM = 1 << 151 + + _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_REQ_ELECTRUM] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE) + _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE) OPTION_SHUTDOWN_ANYSEGWIT_REQ = 1 << 26 OPTION_SHUTDOWN_ANYSEGWIT_OPT = 1 << 27 @@ -1249,7 +1256,7 @@ LN_FEATURES_IMPLEMENTED = ( | LnFeatures.VAR_ONION_OPT | LnFeatures.VAR_ONION_REQ | LnFeatures.PAYMENT_SECRET_OPT | LnFeatures.PAYMENT_SECRET_REQ | LnFeatures.BASIC_MPP_OPT | LnFeatures.BASIC_MPP_REQ - | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT | LnFeatures.OPTION_TRAMPOLINE_ROUTING_REQ + | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM | LnFeatures.OPTION_TRAMPOLINE_ROUTING_REQ_ELECTRUM | LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_OPT | LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_REQ | LnFeatures.OPTION_CHANNEL_TYPE_OPT | LnFeatures.OPTION_CHANNEL_TYPE_REQ ) diff --git a/electrum/lnworker.py b/electrum/lnworker.py index be14b92df..1290396d3 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -184,7 +184,7 @@ LNWALLET_FEATURES = BASE_FEATURES\ | LnFeatures.OPTION_STATIC_REMOTEKEY_REQ\ | LnFeatures.GOSSIP_QUERIES_REQ\ | LnFeatures.BASIC_MPP_OPT\ - | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT\ + | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM\ | LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_OPT\ | LnFeatures.OPTION_CHANNEL_TYPE_OPT\ @@ -1531,9 +1531,10 @@ class LNWallet(LNWorker): if is_hardcoded_trampoline(node_id): return True peer = self._peers.get(node_id) - if peer and peer.their_features.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT): - return True - return False + if not peer: + return False + return (peer.their_features.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR)\ + or peer.their_features.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM)) def suggest_peer(self) -> Optional[bytes]: if not self.uses_trampoline(): diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py index cbbc4ab76..df2c930c4 100644 --- a/electrum/tests/test_lnpeer.py +++ b/electrum/tests/test_lnpeer.py @@ -150,7 +150,7 @@ class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]): self.features |= LnFeatures.OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT self.features |= LnFeatures.VAR_ONION_OPT self.features |= LnFeatures.PAYMENT_SECRET_OPT - self.features |= LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT + self.features |= LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM self.features |= LnFeatures.OPTION_CHANNEL_TYPE_OPT self.pending_payments = defaultdict(asyncio.Future) for chan in chans: @@ -1071,7 +1071,7 @@ class TestPeer(TestCaseForTestnet): if mpp_invoice: graph.workers['dave'].features |= LnFeatures.BASIC_MPP_OPT if disable_trampoline_receiving: - graph.workers['dave'].features &= ~LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT + graph.workers['dave'].features &= ~LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM if not bob_forwarding: graph.workers['bob'].enable_htlc_forwarding = False if alice_uses_trampoline: @@ -1165,7 +1165,7 @@ class TestPeer(TestCaseForTestnet): peers = graph.peers.values() if is_legacy: # turn off trampoline features in invoice - graph.workers['dave'].features = graph.workers['dave'].features ^ LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT + graph.workers['dave'].features = graph.workers['dave'].features ^ LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM # declare routing nodes as trampoline nodes electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = { diff --git a/electrum/tests/test_lnutil.py b/electrum/tests/test_lnutil.py index e099f9b87..1fb395ab4 100644 --- a/electrum/tests/test_lnutil.py +++ b/electrum/tests/test_lnutil.py @@ -869,7 +869,7 @@ class TestLNUtil(ElectrumTestCase): self.assertTrue(f1.supports(LnFeatures.PAYMENT_SECRET_OPT)) self.assertTrue(f1.supports(LnFeatures.BASIC_MPP_REQ)) self.assertFalse(f1.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT)) - self.assertFalse(f1.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_REQ)) + self.assertFalse(f1.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_REQ_ELECTRUM)) def test_lnworker_decode_channel_update_msg(self): msg_without_prefix = bytes.fromhex("439b71c8ddeff63004e4ff1f9764a57dcf20232b79d9d669aef0e31c42be8e44208f7d868d0133acb334047f30e9399dece226ccd98e5df5330adf7f356290516fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008762700054a00005ef2cf9c0101009000000000000003e80000000000000001000000002367b880") @@ -896,7 +896,7 @@ class TestLNUtil(ElectrumTestCase): features = LnFeatures(LnFeatures.BASIC_MPP_OPT | LnFeatures.OPTION_STATIC_REMOTEKEY_OPT) self.assertTrue(ChannelType.OPTION_STATIC_REMOTEKEY.complies_with_features(features)) - features = LnFeatures(LnFeatures.BASIC_MPP_OPT | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT) + features = LnFeatures(LnFeatures.BASIC_MPP_OPT | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM) self.assertFalse(ChannelType.OPTION_STATIC_REMOTEKEY.complies_with_features(features)) # ignore unknown channel types diff --git a/electrum/trampoline.py b/electrum/trampoline.py index 2f9d24e87..2253bc06f 100644 --- a/electrum/trampoline.py +++ b/electrum/trampoline.py @@ -102,7 +102,8 @@ def is_legacy_relay(invoice_features, r_tags) -> Tuple[bool, List[bytes]]: """ invoice_features = LnFeatures(invoice_features) # trampoline-supporting wallets: - if invoice_features.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT): + if invoice_features.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR)\ + or invoice_features.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM): # If there are no r_tags (routing hints) included, the wallet doesn't have # private channels and is probably directly connected to a trampoline node. # Any trampoline node should be able to figure out a path to the receiver and