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.
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.
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
.
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.
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.
e85fad166c9b270a3f04582c4342e8618135f5e70af66d4269040bfd9d201d31