Phoenix users never fund channels themselves; it's always the routing node they're connecting to that opens channels to them via pay-to-open. Let's call such a node an LSP (Lightning Service Provider). Note that for now Phoenix only supports one LSP (Acinq).
We will show that this model provides good UTXO privacy for Phoenix users (and good unlinkability between on-chain and off-chain identities).
Here is an overview of the flow when a Phoenix wallet doesn't have inbound liquidity:
Phoenix LSP Any node
| | add_htlc |
| |<-------------------------|
| pay_to_open_req | |
|<----------------------| |
| ok | |
|---------------------->| |
| open_channel | |
|<----------------------| |
| | fulfill_htlc |
| |------------------------->|
| | |
The incoming payment on the right is a pure lightning payment, without any on-chain footprint.
Let's dive into the open_channel
flow with a focus on the public keys that may be broadcast to
the lightning network or appear in the blockchain:
Phoenix LSP
| open_channel(funding_pubkey_LSP) |
|<-------------------------------------------------|
| accept_channel(funding_pubkey_P) |
|------------------------------------------------->|
| |---+
| | | creates funding tx with spending condition:
| | | `2 <funding_pubkey_LSP> <funding_pubkey_P> 2 OP_CHECKMULTISIG`
| funding_created |<--+
|<-------------------------------------------------|
| funding_signed |
|------------------------------------------------->|
| |---+
| | | broadcast funding tx, wait for `N` confirmations
| funding_locked |<--+
|<-------------------------------------------------|
| funding_locked |
|------------------------------------------------->|
| |
It's important that the channel stays unannounced: that means only the LSP knowns the mapping from
Phoenix's node_id
to its funding_pubkey
. Other network participants cannot link Phoenix's
off-chain activity to the channel's on-chain funding tx.
All the UTXOs that are spent by the funding tx belong to the LSP. That means that the Phoenix user currently has no on-chain footprint whatsoever. Now three things can happen:
- The channel stays open: this is perfect for the Phoenix user, who still doesn't have any on-chain footprint.
- The channel closes after the Phoenix user has emptied his balance (via normal HTLCs or swaps): only the LSP will have UTXOs in the commitment tx, the Phoenix user has no left-over UTXOs so no on-chain footprint. Note that this only works because we allow Phoenix users to have no channel reserve: otherwise Phoenix would always have an UTXO to redeem from the commitment tx.
- The LSP does an uncooperative channel close while the Phoenix user has a non-empty balance:
the Phoenix user now has an on-chain footprint. However only the LSP can link it with his
off-chain
node_id
and won't be able to link it to other on-chain UTXOs. Note that this costs fees only for the LSP (because the LSP is the channel funder) and adds delays for the LSP's outputs.
While the channel is open, the only way the Phoenix user will generate an on-chain footprint is via swaps (swap-in/swap-out).
When swapping-in, the Phoenix user should use a different LSP than the one with whom she has a
channel, and should use a decoy channel_id
and node_id
(e.g. here).
Let's call LSP the node with whom Phoenix has a channel and SSP the swap provider.
SSP can link the swap UTXOs to a node_id
, but this node_id
is a decoy (not the real Phoenix node_id
).
LSP simply forwards an HTLC to Phoenix and has no way of linking it to the swap UTXOs (it can't
even know this HTLC is coming from a swap).
The only way to restore the link is to have LSP and SSP collide. This can be avoided by running your own SSP (or LSP).
When swapping-out, the Phoenix user should use a different LSP than the one with whom she has a channel. Let's call it SSP.
SSP can link the swap UTXOs to a payment_hash
(but not to a node_id
).
LSP simply forwards an HTLC and has no way of linking it to the swap UTXOs (it can't even know this
HTLC will trigger a swap).
The only way to restore the link is to have LSP and SSP collide. This can be avoided by running your own SSP (or LSP).
Phoenix users should use Tor. It allows them to frequently change their node_id
and present a
different identity to the LSP (or manage a set of multiple node_id
s).
Otherwise the LSP could link a node_id
to a set of IP addresses (in particular if the user
connects from his home or work WiFi).
Without Tor the LSP could even match on-chain and off-chain activity simply by matching IP addresses.
It seems to me that even if Phoenix funded the channel, it would still benefit from almost the same
privacy. The important point is to keep the channel unannouced to avoid leaking the
funding_pubkey
<-> node_id
mapping to the whole network.
The only difference is that if the channel closes, Phoenix will have a new UTXO that is linked to the UTXOs used to open the channel. But it can't be linked to off-chain activity.
If you are using a mobile wallet, the "full nodes" you connect to WILL KNOW. There's no point trying to look like a "full node", you just can't. Your mobile won't be always online, won't be able to route payments reliably, won't have enough channels to look like a routing node, etc.
That means these nodes will know that when they route a payment to you, you are the recipient. They will also know that when you route a payment through them, you are the payer. If you don't want to trust any LSP with that information, you should run your own LSP and connect to it. That will give you the best privacy you can achieve.
But that model isn't for every user. This is highly technical and costly to do. However, there are mitigations we can use to ensure you don't leak too much information to the LSPs you choose to connect to.
Phoenix could manage a set of node_id
s (instead of a single one). Used in combination with Tor,
your LSP won't be able to know that multiple node_id
s belong to the same user. You will need to
have channels with each of these node_id
(can't share them as that would leak information).
Why not simply use decoy node_id
s? Because that protects your real node_id
from senders (when
they don't collude with your LSP) but not from your LSP; your LSP will always know the node_id
you used to create channels, which is your long-term identity.
The LSP could watch every UTXO spent/created on-chain to try to guess that HTLCs are in fact swaps. If the LSP forwards an outgoing HTLC for 150500 sats, and then sees on-chain a new UTXO of 150k sats he can guess that it's likely a swap-out. The same heuristic goes for swap-in.
This can be easily defeated by using MPP between Phoenix and the SSP. Simply the fact that MPP is possible already defeats that heuristic.
I think the biggest short-term privacy gains we can aim for are:
Next steps are then:
Longer term is to standardize swap servers to allow many swap servers in the network; that will provide a bigger anonymity set and better unlinkability between on-chain and off-chain activity. When that's done we should let users choose their swap server if they want to (it's better for them to use a different one from the LSP).