-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
routing: send payment metadata #5810
Conversation
record/hop.go
Outdated
|
||
// MetadataOnionType is the type used in the onion for the payment | ||
// metadata. | ||
MetadataOnionType tlv.Type = 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Roasbeef, I've created a draft PR to evaluate touch points. Do you want to go for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes a ton of sense IMO, super useful for the AMP recurring case as well. Could also bundle it w/ the pubkey
based routing stuff as well...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it useful for the AMP recurring case actually? The sending of metadata I get. This could be something like a donation comment. But what would be the purpose of embedding metadata in the payment request? Recurring payment senders would all pass through that same metadata, but what does the receiver learn from this that they didn't already know?
396eca0
to
916ad5f
Compare
904ce49
to
bc28d7e
Compare
@@ -938,6 +938,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash, | |||
customRecords: payload.CustomRecords(), | |||
mpp: payload.MultiPath(), | |||
amp: payload.AMPRecord(), | |||
metadata: payload.Metadata(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is missing here is that we are assuming that an invoice already exists. The goal of the new metadata field is to enable stateless invoices. In that case, there isn't an invoice yet. It is a kind of spontaneous payment (not really spontaneous - the receiver just 'forgot' the invoice), but the difference with keysend/amp is that the receiver is able to re-derive the preimage. The preimage can be used as proof of payment.
I am wondering what the options are to implement this. Will it be yet another payment type? Or is this the point where it becomes necessary to allow invoice registry plugins so that users can implement the logic themselves?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LDK approach with this: lightningdevkit/rust-lightning#1171
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering what the options are to implement this. Will it be yet another payment type
I think we can just use the existing keysend flow here, which ends up inserting the the database, or using the existing AMP pattern to re-insert a new sub-invoice with a main tracking identifier. IIUC, with the proposal you'd still write the invoice to disk, but the "stateless" part is what lets you not have to remember the invoice until its paid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, the existing HTLC interceptor logic can handle this as well assuming you have the invoice logic already living outside of lnd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think "stateless" can be implemented in multiple ways, but doing a JIT insert similar to keysend seems definitely one of the options.
ed27191
to
7b691dd
Compare
7b691dd
to
13fe949
Compare
13fe949
to
86a3c59
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minimal change for a nifty feature!
cmd/lncli/cmd_invoice.go
Outdated
@@ -26,6 +26,11 @@ var addInvoiceCommand = cli.Command{ | |||
Usage: "a description of the payment to attach along " + | |||
"with the invoice (default=\"\")", | |||
}, | |||
cli.StringFlag{ | |||
Name: "metadata", | |||
Usage: "the hex-encoded metadata that will be sent " + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why hex-encoded on the cli? Depending on the sort of values we're expecting here, I'd think that most people would just want to provide a string + have it hex-encoded on their behalf by the cli?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it depends on what the purpose of the metadata is. With this PR, we can't reap the fruits of the metadata yet. It is only preparatory. Yes, we store the metadata in the invoice database upon receipt, but I don't think that is much different from just using description to store additional data locally.
A next step is to no longer store the invoices that we create in the invoice registry. Then when a payment comes in, we use the metadata in the tlv to construct and insert an invoice just-in-time. One of the things that metadata can contain is a preimage encrypted to ourselves.
In those kind of setups, I'd expect metadata to be structured, machine-readable data. Therefore the hex encoding.
// PaymentMetadataRequired is a required bit that denotes that an | ||
// invoice contains metadata that must be passed along with the payment | ||
// htlc(s). | ||
PaymentMetadataRequired = 48 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw there was some discussion in the spec PR about this, seems like the conclusion was to add the optional feature bit as well? Even if we're not going to use it, might be worth adding it here? (more confusing to not have it, than have it and not use it IMO)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the optional feature bit.
The Eclair team is going to use this to get an idea of the proportion of senders that have upgraded to node software that supports metadata sending. They want to include some metadata with every invoice generated and then observe the incoming htlcs to see if it is echoed in tlv.
This is something for us to decide on too. Do we want to increase the size of every invoice slightly to include the marker metadata and the optional bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its clearer to just have it there, rather than explain why it's not there.
This is something for us to decide on too. Do we want to increase the size of every invoice slightly to include the marker metadata and the optional bit?
Wouldn't this decision fall on implementors rather than lnd itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this PR as is, there is no way they can add that marker metadata.
86a3c59
to
0ce379c
Compare
I've marked the last commits as Real receiver-side logic for stateless invoices needs more thinking, but at least with this PR we can start the upgrade process for senders. |
e9585cd
to
c2fa9aa
Compare
cb00b8f
to
f67bc51
Compare
Added test coverage |
f67bc51
to
2ae37a0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only minor comments. I think you can drop the to remove
commits and I'll do a final approval pass!
zpay32/invoice_test.go
Outdated
@@ -726,6 +751,10 @@ func TestDecodeEncode(t *testing.T) { | |||
} | |||
|
|||
if decodedInvoice != nil { | |||
if test.implicitDest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment to explain this? I'm not quite following
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed again. I discovered that there was already a mechanism for this with the beforeEncoding
closure.
// PaymentMetadataRequired is a required bit that denotes that an | ||
// invoice contains metadata that must be passed along with the payment | ||
// htlc(s). | ||
PaymentMetadataRequired = 48 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its clearer to just have it there, rather than explain why it's not there.
This is something for us to decide on too. Do we want to increase the size of every invoice slightly to include the marker metadata and the optional bit?
Wouldn't this decision fall on implementors rather than lnd itself?
3a656ce
to
1c34595
Compare
1c34595
to
348b9ed
Compare
Rebased. As a reminder, this PR is a small change that unlocks interesting new ways to accept payments statelessly. It does require senders on the network to upgrade, which takes time. It would be great to include this in the next release. Other implementations are moving too: ElementsProject/lightning#5086 @carlaKC is this PR still on the safe side for the 0.15 cut and does it need another reviewer? |
@carlaKC: review reminder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only thing I think this is missing is extending the InterceptedPacket
type with the payment metadata field (as is since this isn't a "custom" record, it won't show up) to also add this field. With this addition, then the full "stateless" invoice concept can be fully realized with a custom HTLC interceptor.
@@ -164,6 +164,17 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error { | |||
} | |||
} | |||
|
|||
if invoice.Metadata != nil { | |||
base32, err := bech32.ConvertBits(invoice.Metadata, 8, 5, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
writeBytes32
can be used here instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
writeBytes32
always writes 32 bytes, where as the invoice metadata is variable length?
@@ -938,6 +938,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash, | |||
customRecords: payload.CustomRecords(), | |||
mpp: payload.MultiPath(), | |||
amp: payload.AMPRecord(), | |||
metadata: payload.Metadata(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering what the options are to implement this. Will it be yet another payment type
I think we can just use the existing keysend flow here, which ends up inserting the the database, or using the existing AMP pattern to re-insert a new sub-invoice with a main tracking identifier. IIUC, with the proposal you'd still write the invoice to disk, but the "stateless" part is what lets you not have to remember the invoice until its paid?
@@ -938,6 +938,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash, | |||
customRecords: payload.CustomRecords(), | |||
mpp: payload.MultiPath(), | |||
amp: payload.AMPRecord(), | |||
metadata: payload.Metadata(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, the existing HTLC interceptor logic can handle this as well assuming you have the invoice logic already living outside of lnd.
9fd64ab
to
52cecfa
Compare
I don't think this is needed. HTLCs can't be intercepted at the exit hop, so anything that is intercepted will be a forward. For external invoice handling, the intercepted forward will generally be to the exit hop. This means that the forwarding node, where the interceptor is connected to, won't be able to decrypt the payment metadata. It is still packed in the next hop payload in encrypted form and this payload is already available to the interceptor. |
52cecfa
to
3730002
Compare
3730002
to
62ae038
Compare
Ah true, and even if you used hop hints to have the node be seen as the penultimate hop to the sender, the payload itself is in the onion packet which is already available and can be decrypted by the interceptor if things are well formed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 💐
Implements lightning/bolts#912
Please attach comments to the most appropriate line of the diff (or a random line if none is appropriate), so that discussions remain threaded and can eventually be resolved.