Skip to content

Instantly share code, notes, and snippets.

@callebtc
Last active February 27, 2023 10:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save callebtc/a6cc0bd2b6f70e081e478147c40fc578 to your computer and use it in GitHub Desktop.
Save callebtc/a6cc0bd2b6f70e081e478147c40fc578 to your computer and use it in GitHub Desktop.
Cashu handing of unpredictable fees for Lightning payments

In this document, we describe how we can optimize (i.e., minimize) the fees a Cashu wallet has to pay the mint for making a Lightinig payment with an unpredictable network fee. We do this with the introduction of so-called blank outputs which are blinded messages with an undetermined value.

Problem description

In Lightning, we usually don't know the network fees required for paying an invoice in advance. In many Lightning node implementations and user-facing applications, a maxium fee reserve is defined before an invoice payment is attempted to limit the maximum fee risk of a payment.

This makes it challenging to design an ecash protocol that can handle Lightning payments with variable (and thus unpredictable) fees since the ecash for a payment plus potential Lightning network fees need to be provided upfront before the payment is attempted. Since ecash is not divisible, returning ecash for overpaid fees is impractical. Therefore, in Cashu, what we do right now is to simply provide the fee reserve in addition to the Invoice amount being paid and the mint pockets the difference between the actual fee and the provided fee.

Example

Here is an example: The user wallet wants to pay an invoice with amount = 300 sats. The wallet asks the mint what the necessary fees are for paying an invoice by calling the endpoint /checkfees. The mint either responds with 0 sats if the invoice is detected as internal (meaning no Lightning network transaction is actually necessary) or with a predefined fee_reserve := max(2 sats, 0.01*amount) = 3 sats. The wallet then sends proofs (= ecash) with a total value of amount + fee_reserve = 303 sats to the mint in return for it to pay the Lightning invoice. The mint pays the invoice and determines that it paid fee_actual = 1 sat (always smaller or equal to fee_reserve) only after the fact. The mint pockets difference fee_reserve - fee_actual = 2 sat.

Solution

We now describe how this scneario can be improved with the introduction of blank outputs. When asking the mint to pay a Lightning invoice, in addition to the proofs of the value amount + fee_reserve, a wallet additionally provides a predefined number n_blank_outputs of blank outputs which are blinded messages without an amount. The predefined number is chosen to be n_blank_outputs := 4 which means that we can return at minimum 93.75% (but up to 100%) of the overpaid fees back to the user.

Here is how it works:

The wallet asks the mint for the fee_reserve for paying a specific invoice, like in the example above. The wallet then provides a MeltRequest to /melt that has 1) proofs of the value amount+fee_reserve, 2) the invoice to be paid, and 3) a new field outputs that has n_blank_outputs blinded messages that are generated before the payment attempt.

The mint pays the invoice and determines that the fee was fee_actual and calculates the difference fee_return := fee_reserve - fee_actual that the user overpaid. The mint splits the amount fee_return into summands of value 2^n as we also split other values in Cashu. The mint then takes the up to n_blank_outputs largest of these summands and assigns them to the blank_outputs. It then and signs the blank_outputs to generate new promises, or blinded signatures. The mint then returns a payment status to the wallet, and, in addition, the up to n_blank_outputs blinded signatures it generated for the overpaid fees.

The wallet receives the payment status and the returned promises for the overpaid fees, and unblinds them and saves them in their database. The payment is then considered completed.

Example

The wallet wants to pay an invoice with amount := 100 000 sats and determines by asking the mint that fee_reseve is 1000 sats. The wallet then provides 101 000 sats worth of proofs and 4 blank outputs to make the payment. The mint pays the invoice and determines that the actual fee was 100 sat, i.e, the overpaid fee to return is fee_return = 900 sats. The mint splits the amount 900 into summands of 2^n which is 4, 128, 256, 512. The mint inserts these amounts into the blank_outputs it received form the wallet and generates 4 new promises. The mint then returns these promises to the wallet together with the successful payment status.

@peterzion45
Copy link

e85fad166c9b270a3f04582c4342e8618135f5e70af66d4269040bfd9d201d31

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment