bolts
bolts copied to clipboard
BOLT04: Atomic Multi-path Payments [feature 30/31]
Hi all, decided to submit this draft concurrently with @rustyrussell's Base AMP since there is likely a bit of overlap, and comments can likely be shared between the two. The structure of the proposal and requirements is based on #643 to make that process simpler. The chosen feature bits are just temporary.
Background
This proposal outlines the requirements for Atomic Multi-path Payments originally outlined in @Roasbeef and I's email to lightning-dev. The proposal has also been referred to as OG AMP, or Moon AMP due to the moon-math involved :)
The core of the proposal is mostly unchanged, though I will highlight some distinctions:
- The Extra Onion Blob (EOB) concept has been ditched in favor of the variable-sized hop-payloads via #619
- Termination is signaled via
total_msat
(as in Base AMP) instead of a transmitting the number of shares. This is more flexible when sending partial payments adaptively, since the required number of partial payments may not be known upfront, while the total amount being paid should be known. Committing to the total amount being paid also eliminates weaknesses in zero-value invoices, or any scenario involving overpayment. - The 11-byte payment ID is replaced with a full 32-byte
set_id
- Various modifications in nomenclature
Summary
One of the biggest distinctions (some would argue drawback) from Base AMP is that the sender does not receive a preimage as a result of a successful payment. However, the lack of a "proof" of payment unlocks a range of novel features:
- Partial Payment Decorrelation: each subpayment uses a different payment hash, offering stronger privacy from intermediaries
- Concurrent-Safe Reusable Invoices: As a by-product of the sender generating the preimages and payment hashes, an invoice is no longer single use. The payer conveys to the payee which invoice is being settled by including the invoice's payment hash. All payments made to the invoice can be tracked as a single subscription or account, and each payment receives it's own unique
set_id
. Multiple, concurrent payments can be made to the same invoice and properly reconstructed using the enclosedset_id
.- A subscription invoice might be generated as an invoice with the desired subscription amount, which will be verified each time the user pays.
- An account invoice, e.g. a recurring invoice for funding an exchange, would use a zero-value invoice, allowing deposits (or withdrawals) of arbitrary amounts.
- Spontaneous AMPs: More often discussed in the single-shot case, this describes users making payments to other nodes using just their public key (handwaves hop hints). However, AMP is really a generalization of the single-shot case, so we propose to merge these two concepts under a single, unified proposal. All of the tangential benefits of AMP are carried over to spontaneous single-shot payments (SSSP?), without introducing any additional code complexity.
Open Questions
- Some of the fields included in
option_amp
are to be included in other proposals as well. Should we reuse thetotal_msat
field fromoption_mpp
, which can make use of the truncated encoding? Similarly, theset_id
is analogous to the proposal to generically include a nonce in all payments, should that be it's own record? The signaling of which feature you are attempting to use may be more complex, but it's RISC-ier in some sense.
Feedback appreciated!
Some of the fields included in option_amp are to be included in other proposals as well. Should we reuse the total_msat field from option_mpp, which can make use of the truncated encoding?
I think that makes sense, as you can consider this to be a super set of the basic mpp in a sense. If we go in this direction, then should we switch to more of an "open coded" format for the AMP payload? This direction allows different use protocols to share common fields, but then create a blurry distinction between the allocated onion space amongst the various protocols. The other down side is that it consumes more type space, but with 64-bits available, that really isn't a concern IMO.
Similarly, the stream_id is analogous to the proposal to generically include a nonce in all payments, should that be it's own record? The signaling of which feature you are attempting to use may be more complex, but it's RISC-ier in some sense.
If we go this route, then we may need to allocate an additional tag to indicate the context, so receivers know how to interpret this field. In the MPP case, it's just to be matched against the invoice, while in the AMP case, it may need to be passed to the receiver via some sort of hook for additional validation (the account ID had actually authorized a deposit, etc).
Could a proof of payment for atomic multi-path payments be achieved by locking the htlcs to two hashes (logical AND in the bitcoin script)? In order to settle such an htlc, the receiver needs to reveal both the preimage of the payment hash in the invoice and the preimage that was generated by the sender and embedded in the final onion payload.
Great stuff @cfromknecht I think this adds very valuable features.
One quick thought about share failures.
Imagine the sender uses 3 shares and one fails. In order to preserve r
, the sender has to either re-use the same share (with a different index of course) or split that share into multiple payments. If it's the recipient that failed one of the shares, the recipient will have learnt r
. Could this be an issue (if for example the recipient has other nodes that may end up being intermediate nodes that route shares)?
Not sure it can be exploited, but just putting it out there to make sure it's obvious to everyone.
Okay I tried to respond to all of the immediately actionable comments (lmk if i've missed anything).
The major things that still seem in flux:
- should we reuse
option_mpp
to encodetotal_msat
? i'm leaning towards yes - should we have an additional option for set_id/nonce? part of me thinks it should really be a part of
option_mpp
, but if we also plan to use a nonce for all payments then maybe it should be its own feature
Could a proof of payment for atomic multi-path payments be achieved by locking the htlcs to two hashes (logical AND in the bitcoin script)? In order to settle such an htlc, the receiver needs to reveal both the preimage of the payment hash in the invoice and the preimage that was generated by the sender and embedded in the final onion payload.
Yes I think it is possible, but also requires the entire path to be upgraded before it can be used, unlike the current proposal which only requires sender/receiver to upgrade. If one really wants proof of payment tho, it's probably better to just use #643, since by adding the payment hash in all scripts along the path you lose decorrelation (and reveal that you're using AMP).
Is there a problem with the receiver reading the share value, but then canceling the htlc with for example an invalid onion key error? To the sender is looks like a failure that may even have been caused by the second last node, but in reality the receiver has already obtained the root seed.
If the receiver gets a subpayment and then cancels it back it can learn the root seed, but also won't have a payment to settle. At worst, the sender may try another shard (or split further into subshards), but the total amounts should never exceed what was already presented. In this sense it behaves like base amp, except that only the receiver can initiate the settlement (if they aren't colluding w/ intermediaries), instead of any intermediary who already knows the preimage to a subpayment.
Imagine the sender uses 3 shares and one fails. In order to preserve r, the sender has to either re-use the same share (with a different index of course) or split that share into multiple payments. If it's the recipient that failed one of the shares, the recipient will have learnt r. Could this be an issue (if for example the recipient has other nodes that may end up being intermediate nodes that route shares)?
Related to the above, if the receiver is actually in control of an intermediate node then it can perform a wormhole attack. But idt it is any different from what can already be done (for regular payments or base amp) when using payment hashes, and will eventually be fixed by moving to DLOG challenges :)
should we have an additional option for set_id/nonce? part of me thinks it should really be a part of option_mpp, but if we also plan to use a nonce for all payments then maybe it should be its own feature
OTOH I would propose that we plan to transition all payment types over to mpp or amp, and deprecate the existing single-shot payments. Both proposals support sending 1 shard, and both schemes address known issues in the current single-shot payment flow.
In that case a reasonable split of the tlv records might be:
1. type 8 (`option_mpp`)
2. data:
* [`32*byte`:`set_id`]
* [`u64`:`total_msat`]
1. type: 10 (`option_amp`)
2. data:
* [`32*byte`:`payment_id`]
* [`32*byte`:`share`]
* [`u16`:`child_index`]
Based on discussion in #643, mpp will include a receiver-generated nonce rather than a sender-generated one. Therefore the prior split would be better defined as:
1. type 8 (`option_mpp`)
2. data:
* [`32*byte`:`payment_addr`]
* [`u64`:`total_msat`]
1. type: 10 (`option_amp`)
2. data:
* [`32*byte`:`set_id`]
* [`32*byte`:`share`]
* [`u16`:`child_index`]