Skip to content

Reintroduce support for the old submarine swap protocol#10650

Open
MartyIX wants to merge 7 commits into
spesmilo:masterfrom
MartyIX:wex/swap-server-2026-04-28-9b26c18
Open

Reintroduce support for the old submarine swap protocol#10650
MartyIX wants to merge 7 commits into
spesmilo:masterfrom
MartyIX:wex/swap-server-2026-04-28-9b26c18

Conversation

@MartyIX
Copy link
Copy Markdown

@MartyIX MartyIX commented May 15, 2026

Closes #10611

This PR

  • Reintroduces the old v1 flow for submarine swaps.
    • A preliminary check ensures the LN payment route has a high chance of success before proceeding.
  • Updates the swap server to signal support for forwardv1 (the old protocol).

Motivation

We've been trying to expand Electrum Swap ecosystem by creating interface – https://whales.exchange – for people who do not use Electrum Wallet. We have released the first public version of https://whales.exchange where we implemented support for reverse swaps. We would like to support forward swap as well in order to provide complete swap gateway to Electrum Swaps.

The current implementation of submarine swaps in Electrum require control over user's LN wallet which https://whales.exchange does not control (see the design). However, Electrum used to implement a different protocol, so called old flow or simply v1 flow, for submarine swaps which did not have the requirement. The old implementation was removed because:

The old flow was removed because servers sometimes fail to find a route for the LN payment, in which case users have to get an on-chain refund (funds locked + mining fees). The new flow cannot be supported with bolt11 invoices; it requires a wallet that supports hold invoices.

This PR attempts to bring back the old flow, so Electrum would support the old and the new submarine protocols. Moreover, to address the above issue, a new preliminary "is-there-a-LN-payment-route" check was added to somewhat mitigate the issue.

A short description of both protocols follows:

Old submarine swap flow (v1) is

  • User generates an LN invoice with RHASH, and knows preimage.
  • User creates on-chain output locked to RHASH.
  • Server checks if there is a LN payment route, otherwise the swap is rejected [new]
  • Server pays LN invoice. By completing payment, user reveals preimage.
  • Server spends the on-chain output using preimage.

New submarine swap flow is:

  • User requests swap (RPC 'createnormalswap')
  • Server creates preimage, sends RHASH to user
  • User creates hold invoice, sends it to server (RPC 'addswapinvoice')
  • Server sends HTLC, user holds it
  • User creates on-chain output locked to RHASH
  • Server spends the on-chain output using preimage (revealing the preimage)
  • User fulfills HTLC using preimage

Discussions

(edit: Rebased to b9be974. Originally, it was based on 9b26c18.)

@MartyIX MartyIX force-pushed the wex/swap-server-2026-04-28-9b26c18 branch from 9762103 to 1f587fd Compare May 15, 2026 11:39
Comment thread electrum/lnworker.py
Comment on lines +2030 to +2048

can_route = False

# 2. send htlcs
async for sent_htlc_info, cltv_delta, trampoline_onion in routes:
if probe_only:
can_route = True
continue

await self.pay_to_route(
paysession=paysession,
sent_htlc_info=sent_htlc_info,
min_final_cltv_delta=cltv_delta,
trampoline_onion=trampoline_onion,
fw_payment_key=fw_payment_key,
)

if probe_only:
return can_route
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you are not actually probing? HTLCs are not sent, this is just testing if the offline path finder can create some routes at all.

E.g. if Alice is trying to pay Bob, this is just testing that Alice has enough outbound capacity on the direct channel, and that some path can be found in the graph from Alice to Bob based on the gossip and the invoice r hints only. If Alice is using trampoline, it is checking even less, as Alice does not even have the graph.

Consider that Bob has a channel with the ACINQ node, and Alice has a channel with the "Electrum Trampoline" node. Bob likely only generates a bolt11 invoice if the Bob<->ACINQ channel has liquidity. Similarly, Alice knows if the Alice<->"Electrum Trampoline" channel has liquidity. Whether there is liquidity on any path from "Electrum Trampoline" to ACINQ is not checked here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you are not actually probing? HTLCs are not sent, this is just testing if the offline path finder can create some routes at all.

Yes, the check is very basic. It was somewhat intentional to get some feedback.

E.g. if Alice is trying to pay Bob, this is just testing that Alice has enough outbound capacity on the direct channel, and that some path can be found in the graph from Alice to Bob based on the gossip and the invoice r hints only. If Alice is using trampoline, it is checking even less, as Alice does not even have the graph.

Consider that Bob has a channel with the ACINQ node, and Alice has a channel with the "Electrum Trampoline" node. Bob likely only generates a bolt11 invoice if the Bob<->ACINQ channel has liquidity. Similarly, Alice knows if the Alice<->"Electrum Trampoline" channel has liquidity. Whether there is liquidity on any path from "Electrum Trampoline" to ACINQ is not checked here.

Thank you for the elaboration. Please help me understand if you find this to be a hard requirement for this PR, or not. I'm not sure how to add this behavior at the moment (perhaps @dejfcz does). If you do know how to add it, any pointer would be very helpful and much appreciated. It would save us time. :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some path can be found in the graph from Alice to Bob based on the gossip

Yes. The suggestion is to start with something trivial, which can later be improved by someone with deeper knowledge of Electrum code base. As it is now, it does prevent accepting swap in some scenarios, but of course full probing would be much better. So it is more about setting up the interface (probe_only parameter) for now.

@MartyIX MartyIX force-pushed the wex/swap-server-2026-04-28-9b26c18 branch from 3223fc6 to 0a9d13e Compare May 15, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Electrum Swap,WEX] Third party client forward swaps require different protocol on swap server side

3 participants