Description
Currently we require the user register all inbound payments before we receive them so that we can authenticate them using the payment secret. This is kinda annoying, we have to store a bunch of payment state and time that out appropriately based on user-provided timeout data (via block timestamps...).
This is maybe all fine except its a good bit of complexity and we'd like to avoid it if possible. In lightning/bolts#912, Joost suggested lightning nodes just take their local payment metadata and encrypt it to themselves (with an HMAC), then have the sender send us that data in the HTLC onion. This way, we make the sender (via the invoice) deal with tracking our payment metadata and we don't actually have to store it at all.
For most lightning clients this is probably annoying cause they have material payment metadata, but, we don't - we (currently, see PendingInboundPayment
) only care about the payment hash/preimage, payment secret, amount, expiry, and user_payment_id. If we drop the last one, I believe we can trivially just start doing this today, except via the payment secret and not a new field.
There is a bit of complexity here, however, because we have effectively two different payment types - those from create_inbound_payment
where we constructed the payment preimage, and create_inbound_payment_for_hash
where the user gave us the payment hash. This is rectifiable, however.
To shove all the needed info in the payment secret, we'd replace the payment secret with an encrypted blob. That encrypted blob would be different based on the type of payment:
For create_inbound_payment
payments, we'd set it to contain:
* the minimum amount field - 8 bytes
* the high bit of the minimum amount field is 0
* the payment expiry timestamp - 8 bytes
* 16 bytes of random data
Then, we'd take an HMAC of the above and set the payment preimage to the HMAC of the above. We'd (of course) hash the payment preimage as the payment hash. Thus, when we receive a payment, we take the payment secret, we HMAC it, we hash the HMAC and we check that the payment hash matches - if it does, we've effectively HMAC'd the data and know its ours and process using the encrypted metadata, otherwise we fail the payment.
For create_inbound_payment_for_hash
we do something similar, but:
- instead of the high bit of the minimum amount field being 0, we set it to 1, which allows us to identify the type of payment
- instead of 16 bytes of random data we set it to HMAC(payment_hash || minimum amount field || expiry field)
Thus, upon receipt, we re-generate the now-explicit HMAC and check that it matches the metadata and payment hash.
All of this requires that we drop the user_payment_id
, but @valentinewallace's understanding from asking our users is none of them use it, and they can always store local payment metadata against the payment hash itself.