[Lightning-dev] Sphinx Rendezvous Update

Christian Decker decker.christian at gmail.com
Mon Mar 2 11:39:49 UTC 2020

Hi Bastien,

thanks for verifying my proposal, and I do share your concerns regarding
privacy leaks (how many hops are encoded in the onion) and success ratio
if a payment is based on a fixed (partial) path.

> I believe this makes it quite usable in Bolt 11 invoices, without blowing up
> the size of the QR code (but more experimentation is needed on that).

It becomes a tradeoff of how small you want your onion to be, and how
many hops the partial onion can have. For longer partial onions we're
getting close to the current full onion size, but I expect most partial
onion to be close to the network diameter of ~6 (excluding degerenate
chains). So the example below with 5 hops seemed realistic, and dropping
the legacy format in favor of TLVs we can get a couple of bytes back as

>> As an example such an onion, with 5 legacy hops (65 byte each) results
>> in a 325 + 66 bytes onion, and we save 975 bytes.
> While having flexibility when choosing the length of the prefill
> stream feels nice, wouldn't it be safer to impose a fixed size to
> avoid any kind of heuristic at `RV` to try to guess how many hops
> there are between him and the recipient?

I'm currently just using the maximum size, which is an obvious privacy
leak, but I'm also planning on exposing the size to be prefilled, and
hence cropped out when compressing, when generating. Ideally we'd have a
couple of presets, i.e., 1/4, 2/4, 3/4, and adhere to them, randomizing
which one we pick.

Having smaller partial onions would enable my stretch goal of being able
to chain multiple partial onions, though that might be a useless
achievement to unlock xD

>> Compute a shared secret using a random ephemeral private key and
>> `RV`s public key, and then generate a prefill-key
> While implementing, I felt that the part about the shared secret used
> to generate the prefill stream is a bit blurry (your proposal on
> Github doesn't phrase it the same way). I think it's important to
> stress that this secret is derived from both `ephkey` and `RV`'s
> private key, so that `RV+1` can't compute the same stream.

I noticed the same while implementing the decompress stage, which
requires the node ID from `RV` during generation, and performs ECDH +
HKDF with the `RV` node private and the ephemeral key in the *next*
onion, i.e., the one extracted from the payload itself. This is
necessary since the ephemeral key on the incoming onion, which delivered
the partial onion in its payload is not controlled by the partial onion
creator, while the one in the partial onion is.

This means that the ephemeral key in the partial onion is used twice:

 - Once by `RV` to generate the obfuscation stream to fill in the gap
 - As part of the reconstructed onion, processed by `RV+1` to decode the

I'm convinced this is secure and doesn't leak information since
otherwise transporting the ephemeral key publicly would be insecure
(`RV+1` can't generate the obfuscation secret used to fill in the gap
without access to `RV`s private key), and the ephemeral key is only
transmitted in cleartext once (from `RV` to `RV+1`), otherwise it is
hidden in the outer onion.

> Another thing that may be worth mentioning is error forwarding. Since
> the recipient generated the onion, `RV` won't have the shared secrets
> (that's by design). So it's expected that payment errors won't be
> readable by `RV`, but it's probably a good idea if `RV` returns an
> indication to the sender that the payment failed *after* the
> rendezvous point.

Indeed, this is pretty much by design, since otherwise the sender could
provoke errors, e.g., consuming all of `RV`s outgoing capacity with
probes to get back temporary channel failure errors for the channel that
was encoded in the partial onion, and then do that iteratively until we
have identified the real destination which we weren't supposed to learn.

So any error beyond `RV` should be treated by the sender as "rendez-vous
failed, discard partial onion".

> An important side-note is that your proposal is really quick and
> simple to implement from the existing Sphinx code. I have made ASCII
> diagrams of the scheme (see [1]).  This may help readers visualize it
> more easily.

I quickly skimmed the drawings and they're very nice to understand how
regions overlap, that was my main problem with the whole sphinx
construction, so thanks for taking the time :+1:

> It still has the issue that each hop's amount/cltv is fixed at invoice
> generation time by the recipient. That means MPP cannot be used, and
> if any channel along the path updates their fee the partial onion
> becomes invalid (unless you overpay the fees).
> Trampoline should be able to address that since it provides more
> freedom to each trampoline node to find an efficient way to forward to
> the next trampoline.  It's not yet obvious to me how I can mix these
> two proposals to make it work though.  I'll spend more time
> experimenting with that.

True, I think rendez-vous routing have some use-cases, but routing in
the public network seems a bit brittle. It is definitely not MPP
compliant since the sphinx constructions says that each shared secret
should be blacklisted after use, and if we were to use the partial onion
on multiple path we'd be bound to use the same shared secret for the
subpaths contained in the partial onion.

I've been mostly thinking about systems in which you can guarantee
stability, e.g., in a subnetwork controlled by the recipient, but to
hide any internal routing decisions. That'd be similar to a supercharged
routing hint basically, without revealing the internal structure.


More information about the Lightning-dev mailing list